v13i039: unclutter V2, Part01/01

Mark Martin mmm at cetia.fr
Sun May 26 07:55:14 AEST 1991


Submitted-by: Mark Martin <mmm at cetia.fr>
Posting-number: Volume 13, Issue 39
Archive-name: unclutter2/part01

This is a second version of unclutter which uses a different method to
set the cursor to blank.  It creates a sub-window instead of grabbing
the pointer.  It works better with window managers like olvwm.  The
patches are bigger than the complete thing, so here it is complete.

unclutter is a program which runs permanently in the background of an
X11 session.  It checks on the X11 pointer (cursor) position every few
seconds, and when it finds it has not moved it installs an invisible
cursor.  This allows you to see all the text in an xterm or xedit, for
example.  The human factors crowd would agree it should make things
less distracting.
--
Mark M Martin			mmm at cetia.fr
Cetia (BP 244),			inria!cetia!mmm
150 rue Marcelin Berthelot,	tel +33 94 08 80 00
ZI Toulon Est,			fax +33 94 08 80 01
83078 TOULON CEDEX 9, France

#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	unclutter/unclutter.c unclutter/unclutter.man unclutter/vroot.h unclutter/Makefile unclutter/README unclutter/Imakefile unclutter/patchlevel.h
# This archive created: Thu May 16 16:34:34 FDT 1991
# By: mmm
# Part 1 of 1
PATH=/bin:$PATH export PATH
mkdir unclutter
if test -f 'unclutter/unclutter.c'
then	echo "shar: will not overwrite existing file unclutter/unclutter.c"
else	echo "shar: extracting unclutter/unclutter.c (6666 chars)"
	sed 's/^X//' <<\END-OF-FILE! >'unclutter/unclutter.c'
X/*
X * unclutter: remove idle cursor image from screen so that it doesnt
X * obstruct the area you are looking at.
X * doesn't do it if cursor is in root window or a button is down.
X * Tries to cope with jitter if you have a mouse that twitches.
X * (Could be a little fancier and only do it in particular clients.)
X * Unfortunately, clients like emacs set different text cursor
X * shapes depending on whether they have pointer focus or not.
X * Whereas version 1 did a grab cursor, version 2 creates a small subwindow.
X * This may work better with some window managers.
X * Mark M Martin. cetia 1991  mmm at cetia.fr
X */
X#include <X11/Xos.h>
X#include <X11/Xlib.h>
X#include <X11/Xutil.h>
X#include <X11/Xproto.h>
X#include <stdio.h>
X#include "vroot.h"
X
Xchar *progname;
Xpexit(str)char *str;{
X    fprintf(stderr,"%s: %s\n",progname,str);
X    exit(1);
X}
Xusage(){
X    pexit("usage:\n\
X	-display <display>\n\
X	-idle <seconds>		time between polls to detect idleness.\n\
X	-jitter <pixels>	pixels mouse can twitch without moving\n\
X	-grab			use grabpointer method not createwindow\n\
X	-reset			reset the timer whenever cursor becomes\n\
X					visible even if it hasn't moved\n\
X 	-root	       		apply to cursor on root window too");
X}
X
X#define ALMOSTEQUAL(a,b) (abs(a-b)<=jitter)
X#define ANYBUTTON (Button1Mask|Button2Mask|Button3Mask|Button4Mask|Button5Mask)
X
X/* Since the small window we create is a child of the window the pointer is
X * in, it can be destroyed by its adoptive parent.  Hence our destroywindow()
X * can return an error, saying it no longer exists.  Similarly, the parent
X * window can disappear while we are trying to create the child. Trap and
X * ignore these errors.
X */
Xint (*defaulthandler)();
Xint errorhandler(display,error)
XDisplay *display;
XXErrorEvent *error;
X{
X    if(error->error_code!=BadWindow)
X	(*defaulthandler)(display,error);
X}
X
Xmain(argc,argv)char **argv;{
X    Display *display;
X    Cursor cursor;
X    int screen,oldx,oldy = -99;
X    int doroot = 0, jitter = 0, idletime = 5, usegrabmethod = 0, waitagain = 0;
X    Window root;
X    Pixmap cursormask;
X    XGCValues xgc;
X    GC gc;
X    XColor dummycolour;
X    char *displayname = 0;
X    
X    progname = *argv;
X    argc--;
X    while(argv++,argc-->0){
X	if(strcmp(*argv,"-idle")==0){
X	    argc--,argv++;
X	    if(argc<0)usage();
X	    idletime = atoi(*argv);
X	}else if(strcmp(*argv,"-jitter")==0){
X	    argc--,argv++;
X	    if(argc<0)usage();
X	    jitter = atoi(*argv);
X	}else if(strcmp(*argv,"-root")==0){
X	    doroot = 1;
X	}else if(strcmp(*argv,"-grab")==0){
X	    usegrabmethod = 1;
X	}else if(strcmp(*argv,"-reset")==0){
X	    waitagain = 1;
X	}else if(strcmp(*argv,"-display")==0){
X	    argc--,argv++;
X	    if(argc<0)usage();
X	    displayname = *argv;
X	}else usage();
X    }
X    display = XOpenDisplay(displayname);
X    if(display==0)pexit("could not open display");
X    screen = DefaultScreen(display);
X    root = VirtualRootWindow(display,screen);
X
X    /* create a small 1x1 cursor with all pixels masked out */
X    cursormask = XCreatePixmap(display, root, 1, 1, 1/*depth*/);
X    xgc.function = GXclear;
X    gc =  XCreateGC(display, cursormask, GCFunction, &xgc);
X    XFillRectangle(display, cursormask, gc, 0, 0, 1, 1);
X    dummycolour.pixel = 0;
X    dummycolour.red = 0;
X    dummycolour.flags = 04;
X    cursor = XCreatePixmapCursor(display, cursormask, cursormask,
X	      &dummycolour,&dummycolour, 0,0);
X    XFreePixmap(display,cursormask);
X    XFreeGC(display,gc);
X    if(!usegrabmethod)
X	defaulthandler = XSetErrorHandler(errorhandler);
X
X    while(1){
X	Window dummywin,windowin;
X	int rootx,rooty,winx,winy;
X	unsigned int modifs;
X	
X	/* wait for pointer to not move and no buttons down */
X	while(1){
X	    if(!XQueryPointer(display, root, &dummywin, &windowin,
X			 &rootx, &rooty, &winx, &winy, &modifs)){
X		/* window manager with virtual root may have restarted */
X		root = VirtualRootWindow(display,screen);
X	    }else if((!doroot && windowin==None) || (modifs & ANYBUTTON) ||
X		     !(ALMOSTEQUAL(rootx,oldx) && ALMOSTEQUAL(rooty,oldy))){
X		oldx = rootx, oldy = rooty;
X	    }else break;
X	    sleep(idletime);
X	}
X	/* wait again next time */
X	if(waitagain)
X	    oldx = -1-jitter;
X	if(usegrabmethod){
X	    if(XGrabPointer(display, root, 0,
X		    PointerMotionMask|ButtonPressMask|ButtonReleaseMask,
X		    GrabModeAsync, GrabModeAsync, None, cursor,
X		    CurrentTime)==GrabSuccess){
X		/* wait for a button event or large cursor motion */
X		XEvent event;
X		do{
X		    XNextEvent(display,&event);
X		}while(event.type==MotionNotify &&
X		       ALMOSTEQUAL(rootx,event.xmotion.x) &&
X		       ALMOSTEQUAL(rooty,event.xmotion.y));
X		XUngrabPointer(display, CurrentTime);
X	    }
X	}else{
X	    XSetWindowAttributes attributes;
X	    XEvent event;
X	    Window cursorwindow;
X	    Window childin;
X	    
X	    /* descend tree of windows under cursor to bottommost */
X	    if(windowin!=None)
X		while(XQueryPointer(display, windowin, &dummywin, &childin,
X				    &rootx, &rooty, &winx, &winy, &modifs)
X		      && childin!=None)
X		    windowin = childin;
X	    else windowin = root;
X	    /* create small input-only window under cursor
X	     * as a sub window of the window currently under the cursor
X	     */
X	    attributes.event_mask = LeaveWindowMask |
X			VisibilityChangeMask |
X			StructureNotifyMask |
X			FocusChangeMask;
X	    attributes.override_redirect = True;
X	    attributes.cursor = cursor;
X	    cursorwindow = XCreateWindow
X		(display, windowin,
X		 winx-jitter, winy-jitter,
X		 jitter*2+1, jitter*2+1, 0, CopyFromParent,
X		 InputOnly, CopyFromParent, 
X		 CWOverrideRedirect | CWEventMask | CWCursor,
X		 &attributes);
X	    /* discard old events for previously created windows */
X	    XSync(display,True);
X	    XMapWindow(display,cursorwindow);
X	    /* dont wait for expose/map cos override and inputonly? */
X	    /* check that created window captured the pointer */
X	    if(XQueryPointer(display, windowin, &dummywin, &childin,
X			     &rootx, &rooty, &winx, &winy, &modifs)
X	       && childin==cursorwindow){
X		/* wait till pointer leaves window */
X		do{
X		    XNextEvent(display,&event);
X		}while(event.type!=LeaveNotify &&
X		       event.type!=FocusOut &&
X		       event.type!=UnmapNotify &&
X		       event.type!=ConfigureNotify &&
X		       event.type!=CirculateNotify &&
X		       event.type!=ReparentNotify &&
X		       event.type!=DestroyNotify &&
X		       event.type!=VisibilityNotify);
X		/* check if a second unclutter is running cos they thrash */
X		if(event.type==LeaveNotify &&
X		   event.xcrossing.window==cursorwindow &&
X		   event.xcrossing.detail==NotifyInferior)
X		    pexit("someone created a sub-window to my sub-window! giving up");
X	    }
X	    XDestroyWindow(display, cursorwindow);
X	}
X    }
X}
END-OF-FILE!
	if test 6666 -ne "`wc -c <'unclutter/unclutter.c'`"
	then	echo "shar: error transmitting unclutter/unclutter.c (6666 characters)"
	fi
fi
if test -f 'unclutter/unclutter.man'
then	echo "shar: will not overwrite existing file unclutter/unclutter.man"
else	echo "shar: extracting unclutter/unclutter.man (2156 chars)"
	sed 's/^X//' <<\END-OF-FILE! >'unclutter/unclutter.man'
X.\"unclutter man
X.TH UNCLUTTER 1X
X.SH NAME
Xunclutter \- remove idle cursor image from screen
X.SH SYNOPSIS
X.B
Xunclutter
X.RB [ -display
X.IR display ]
X.RB [ -idle
X.IR seconds ]
X.RB [ -jitter
X.IR pixels ]
X.RB [ -grab ]
X.RB [ -reset ]
X.RB [ -root ]
X.SH DESCRIPTION
X.B unclutter
Xremoves the cursor image from the screen so that it does not
Xobstruct the area you are looking at after it has not moved for
Xa given time.
XIt does not do this if the cursor is in the root window or a button is down.
XIt tries to ignore jitter (small movements due to noise)
Xif you have a mouse that twitches.
X.SH OPTIONS
X.TP
X-display
Xis followed by the display to open.
X.TP
X-idle
Xis followed by the number of seconds between polls for idleness.
XThe default is 5.
X.TP
X-jitter
Xis followed by the amount of movement of the pointer that is to be ignored
Xand considered as random noise.
XThe default is 0.
X.TP
X-grab
Xmeans use the original method of grabbing the pointer in order to remove the
Xcursor. 
XThis often doesn't interoperate too well with some window managers.
X.TP
X-reset
Xresets the timeout for idleness after the cursor is restored for some reason
X(such as a window being pushed or popped) even though the x y coordinates
Xof the cursor have not changed.
XNormally, the cursor would immediately be removed again.
X.TP
X-root
Xmeans remove the cursor even if it is on the root background, where in
Xprinciple it should not be obscuring anything useful.
X.SH LIMITATIONS
XUnfortunately, clients like emacs set different text cursor
Xshapes depending on whether they have pointer focus or not,
Xand so not only does the cursor pixture go, but the text cursor image changes.
XEven more sadly, some window managers like to remove keyboard focus if pointer
Xfocus is lost.
X.SH DIAGNOSTICS
XThe message
X.sp
X someone created a sub-window to my sub-window!
X.sp 
Xmeans that unclutter thinks a second unclutter is running, and tried
Xto steal the cursor by creating a sub-window to the sub-window already
Xused to steal the cursor.
XThis situation quickly deteriorates into a fight no one can win, so
Xit is detected when possible and the program gives up.
X.SH AUTHOR
XMark M Martin. cetia 1991. mmm at cetia.fr
END-OF-FILE!
	if test 2156 -ne "`wc -c <'unclutter/unclutter.man'`"
	then	echo "shar: error transmitting unclutter/unclutter.man (2156 characters)"
	fi
fi
if test -f 'unclutter/vroot.h'
then	echo "shar: will not overwrite existing file unclutter/vroot.h"
else	echo "shar: extracting unclutter/vroot.h (2798 chars)"
	sed 's/^X//' <<\END-OF-FILE! >'unclutter/vroot.h'
X/*
X * Article 12523 of comp.windows.x:
X * From: stolcke at ICSI.Berkeley.EDU (Andreas Stolcke)
X * Subject: Converting programs to virtual root
X * Date: 8 Sep 90 02:26:14 GMT
X * 
X * Andreas Stolcke
X * International Computer Science Institute	stolcke at icsi.Berkeley.EDU
X * 1957 Center St., Suite 600, Berkeley, CA 94704	(415) 642-4274 ext. 126
X * 
X * vroot.h -- Virtual Root Window handling header file
X *
X * This header file redefines the X11 macros RootWindow and DefaultRootWindow,
X * making them look for a virtual root window as provided by certain `virtual'
X * window managers like swm and tvtwm. If none is found, the ordinary root
X * window is returned, thus retaining backward compatibility with standard
X * window managers.
X * The function implementing the virtual root lookup remembers the result of
X * its last invocation to avoid overhead in the case of repeated calls
X * on the same display and screen arguments. 
X * The lookup code itself is taken from Tom LaStrange's ssetroot program.
X *
X * Most simple root window changing X programs can be converted to using
X * virtual roots by just including
X *
X * #include "vroot.h"
X *
X * after all the X11 header files.  It has been tested on such popular
X * X clients as xphoon, xfroot, xloadimage, and xaqua.
X *
X * Andreas Stolcke (stolcke at ICSI.Berkeley.EDU), 9/7/90
X * minor mods signalled with: mmm and DONTOPTIMISE.
X */
X#define DONTOPTIMISE	/* unclutter needs to search from scratch each time */
X
X#include <X11/X.h>
X#include <X11/Xatom.h>
X
Xstatic Window
XVirtualRootWindow(dpy, screen)
XDisplay *dpy;
X{
X	static Display *save_dpy = (Display *)NULL;
X	static int save_screen = -1;
X	static Window root = (Window)NULL;
X
X	Atom __SWM_VROOT = None;
X	int i;
X	Window rootReturn, parentReturn, *children;
X	unsigned int numChildren;
X
X#ifdef DONTOPTIMISE
X	{
X#else
X	if ( dpy != save_dpy || screen != save_screen ) {
X#endif
X		root = RootWindow(dpy, screen);
X
X		/* go look for a virtual root */
X		__SWM_VROOT = XInternAtom(dpy, "__SWM_VROOT", False);
X		if(XQueryTree(dpy, root, &rootReturn, &parentReturn,
X				 &children, &numChildren)){	/* mmm */
X		    for (i = 0; i < numChildren; i++) {
X			Atom actual_type;
X			int actual_format;
X			unsigned long nitems, bytesafter;	/* mmm */
X			Window *newRoot = NULL;
X
X			if (XGetWindowProperty(dpy, children[i], __SWM_VROOT,
X				0, 1, False, XA_WINDOW,
X				&actual_type, &actual_format,
X				&nitems, &bytesafter,
X				(unsigned char **) &newRoot) == Success
X			    && newRoot) {
X			    root = *newRoot;
X			    break;
X			}
X		    }
X		    if(children)XFree((char *)children);	/* mmm */
X		}
X		save_dpy = dpy;
X		save_screen = screen;
X	}
X
X	return root;
X}
X
X#undef DefaultRootWindow
X#define DefaultRootWindow(dpy) RootWindow(dpy, DefaultScreen(dpy))
X
X#undef RootWindow
X#define RootWindow(dpy,screen) VirtualRootWindow(dpy,screen)
END-OF-FILE!
	if test 2798 -ne "`wc -c <'unclutter/vroot.h'`"
	then	echo "shar: error transmitting unclutter/vroot.h (2798 characters)"
	fi
fi
if test -f 'unclutter/Makefile'
then	echo "shar: will not overwrite existing file unclutter/Makefile"
else	echo "shar: extracting unclutter/Makefile (1996 chars)"
	sed 's/^X//' <<\END-OF-FILE! >'unclutter/Makefile'
X# Makefile for unclutter.  Throw this away and use the Imakefile if you can.
X              TOP = .
X      CURRENT_DIR = .
X               CC = gcc
X             LKED = $(CC)
X          INSTALL = install
X             MAKE = make
X               MV = mv
X               RM = rm -f
X             TAGS = ctags
X           MFLAGS = -$(MAKEFLAGS)
X     INSTPGMFLAGS = -c -s
X     INSTMANFLAGS = -c
X     TOP_INCLUDES = -I$(INCROOT)
X      CDEBUGFLAGS = -O
X      ALLINCLUDES = $(STD_INCLUDES) $(TOP_INCLUDES) $(INCLUDES) $(EXTRA_INCLUDES)
X       ALLDEFINES = $(ALLINCLUDES) $(STD_DEFINES) $(PROTO_DEFINES) $(DEFINES) $(COMPATFLAGS)
X           CFLAGS = $(CDEBUGFLAGS) $(CCOPTIONS) $(ALLDEFINES)
X           LDLIBS = $(SYS_LIBRARIES) $(EXTRA_LIBRARIES)
X        LDOPTIONS = $(CDEBUGFLAGS) $(CCOPTIONS)
X           RM_CMD = $(RM) *.CKP *.ln *.BAK *.bak *.o core errs ,* *~ *.a .emacs_* tags TAGS make.log MakeOut
X         IRULESRC = $(CONFIGDIR)
X        IMAKE_CMD = $(IMAKE) -DUseInstalled -I$(IRULESRC) $(IMAKE_DEFINES)
X           BINDIR = $(DESTDIR)/usr/bin/X11
X          INCROOT = $(DESTDIR)/usr/include
X          MANPATH = $(DESTDIR)/usr/catman/x11_man
X    MANSOURCEPATH = $(MANPATH)/man
X           MANDIR = $(MANSOURCEPATH)1
X            IMAKE = imake
X             XLIB = $(EXTENSIONLIB)  -lX11
X
X  LOCAL_LIBRARIES = $(XLIB)
X
X OBJS = unclutter.o
X SRCS = unclutter.c
X
X PROGRAM = unclutter
X
Xall:: unclutter
X
Xunclutter: $(OBJS) $(DEPLIBS)
X	$(RM) $@
X	$(LKED) -o $@ $(OBJS) $(LDOPTIONS) $(LOCAL_LIBRARIES) $(LDLIBS) $(EXTRA_LOAD_FLAGS)
X
Xinstall:: unclutter
X	$(INSTALL) -c $(INSTPGMFLAGS)   unclutter $(BINDIR)
Xinstall.man:: unclutter.man
X	$(INSTALL) -c $(INSTMANFLAGS) unclutter.man $(MANDIR)/unclutter.1
Xclean::
X	$(RM) $(PROGRAM)
X	$(RM_CMD) \#*
XMakefile::
X	- at if [ -f Makefile ]; then \
X	echo "	$(RM) Makefile.bak; $(MV) Makefile Makefile.bak"; \
X	$(RM) Makefile.bak; $(MV) Makefile Makefile.bak; \
X	else exit 0; fi
X	$(IMAKE_CMD) -DTOPDIR=$(TOP) -DCURDIR=$(CURRENT_DIR)
Xtags::
X	$(TAGS) -w *.[ch]
X	$(TAGS) -xw *.[ch] > TAGS
END-OF-FILE!
	if test 1996 -ne "`wc -c <'unclutter/Makefile'`"
	then	echo "shar: error transmitting unclutter/Makefile (1996 characters)"
	fi
fi
if test -f 'unclutter/README'
then	echo "shar: will not overwrite existing file unclutter/README"
else	echo "shar: extracting unclutter/README (1276 chars)"
	sed 's/^X//' <<\END-OF-FILE! >'unclutter/README'
Xunclutter is a program which runs permanently in the background of an X11
Xsession.  It checks on the X11 pointer (cursor) position every few
Xseconds, and when it finds it has not moved (and no buttons
Xare pressed on the mouse, and the cursor is not in the root window)
Xit creates a small sub-window as a child of the window the cursor is in.
XThe new window installs a cursor of size 1x1 but a mask of
Xall 0, ie an invisible cursor.  This allows you to see all the text in
Xan xterm or xedit, for example.  The human factors crowd would agree it
Xshould make things less distracting.
X
XOnce created, the program waits for the pointer to leave the window
Xand then destroys it, restoring the original situation.
XButton events are passed transparently through to the parent window.
XThey will usually cause the cursor to reappear because an active grab
Xwill be made by the program while the button is down, so the pointer
Xwill apparently leave the window, even though its x y position doesnt change.
X
XThe first version of this program used a grab to remove the cursor.
XThis methid is still available with a "-grab" option to the program.
X
XThe program is released into the public domain.  Only the considerate
Xwill leave credit for the author.
X
X	Mark M Martin. mmm at cetia.fr  may 1991.
END-OF-FILE!
	if test 1276 -ne "`wc -c <'unclutter/README'`"
	then	echo "shar: error transmitting unclutter/README (1276 characters)"
	fi
fi
if test -f 'unclutter/Imakefile'
then	echo "shar: will not overwrite existing file unclutter/Imakefile"
else	echo "shar: extracting unclutter/Imakefile (59 chars)"
	sed 's/^X//' <<\END-OF-FILE! >'unclutter/Imakefile'
XLOCAL_LIBRARIES = $(XLIB)
X
XSimpleProgramTarget(unclutter)
X
END-OF-FILE!
	if test 59 -ne "`wc -c <'unclutter/Imakefile'`"
	then	echo "shar: error transmitting unclutter/Imakefile (59 characters)"
	fi
fi
if test -f 'unclutter/patchlevel.h'
then	echo "shar: will not overwrite existing file unclutter/patchlevel.h"
else	echo "shar: extracting unclutter/patchlevel.h (278 chars)"
	sed 's/^X//' <<\END-OF-FILE! >'unclutter/patchlevel.h'
X/*
X * unclutter: 
X * level 0: initial release
X * level 1: -grab option to use old method, new method creates small input
X * only sub-window. (distributed by mail only, not posted)
X * level 2: use Andreas Stolcke's vroot.h for tvtwm and similar winmans.
X */
X#define PATCHLEVEL 2
END-OF-FILE!
	if test 278 -ne "`wc -c <'unclutter/patchlevel.h'`"
	then	echo "shar: error transmitting unclutter/patchlevel.h (278 characters)"
	fi
fi
echo 'end of shar part 1 of 1'
exit 0

--
Dan Heller
O'Reilly && Associates       Z-Code Software    Comp-sources-x:
Senior Writer                President          comp-sources-x at uunet.uu.net
argv at ora.com                 argv at zipcode.com



More information about the Comp.sources.x mailing list