code: drawterm

Download patch

ref: 54ca86f879d7f131fa76dc448193caa31feed5ad
parent: a43c1e6fab4d75c8d173a482ee8f4aa04de151c0
author: Russ Cox <rsc@swtch.com>
date: Mon Oct 17 12:18:45 EDT 2005

Rewrite X11 snarf.  Maybe this will be better.

--- a/gui-x11/screen.c
+++ b/gui-x11/screen.c
@@ -16,9 +16,9 @@
  */
 #define	Point	IPoint
 #define	Rectangle	IRectangle
-#define Display	IDisplay
-#define Font	IFont
-#define Screen	IScreen
+#define	Display	IDisplay
+#define	Font	IFont
+#define	Screen	IScreen
 
 #include <u.h>
 #include <libc.h>
@@ -81,7 +81,7 @@
 static	void		xkeyboard(XEvent*);
 static	void		xmapping(XEvent*);
 static	void		xdestroy(XEvent*);
-static	void		xselect(XEvent*);
+static	void		xselect(XEvent*, Display*);
 static	void		xproc(void*);
 static	Memimage*		xinitscreen(void);
 static	void		initmap(Window);
@@ -89,9 +89,9 @@
 static	void		graphicscmap(XColor*);
 	int		xscreendepth;
 	Drawable	xscreenid;
-	Display*	xdisplay;
-	Display*	xkmcon;
-	Display*	xsnarfcon;
+	Display*	xdisplay;	/* used holding draw lock */
+	Display*	xkmcon;	/* used only in xproc */
+	Display*	xsnarfcon;	/* used holding clip.lk */
 	Visual		*xvis;
 	GC		xgcfill, xgccopy, xgcsimplesrc, xgczero, xgcreplsrc;
 	GC		xgcfill0, xgccopy0, xgcsimplesrc0, xgczero0, xgcreplsrc0;
@@ -98,7 +98,9 @@
 	ulong		xblack;
 	ulong		xwhite;
 	ulong	xscreenchan;
-	
+
+static int putsnarf, assertsnarf;
+
 extern Memimage* xallocmemimage(IRectangle, ulong, int);
 Memimage *gscreen;
 Screeninfo screen;
@@ -134,6 +136,7 @@
 void
 flushmemscreen(IRectangle r)
 {
+	assert(!drawcanqlock());
 	if(r.min.x >= r.max.x || r.min.y >= r.max.y)
 		return;
 	XCopyArea(xdisplay, xscreenid, xdrawable, xgccopy, r.min.x, r.min.y, Dx(r), Dy(r), r.min.x, r.min.y);
@@ -160,8 +163,10 @@
 void
 mouseset(IPoint xy)
 {
+	drawqlock();
 	XWarpPointer(xdisplay, None, xdrawable, 0, 0, 0, 0, xy.x, xy.y);
 	XFlush(xdisplay);
+	drawqunlock();
 }
 
 static Cursor xcursor;
@@ -180,6 +185,7 @@
 		mask[i] = revbyte(cursor.set[i] | cursor.clr[i]);
 	}
 
+	drawqlock();
 	fg = map[0];
 	bg = map[255];
 	xsrc = XCreateBitmapFromData(xdisplay, xdrawable, src, 16, 16);
@@ -194,11 +200,13 @@
 	XFreePixmap(xdisplay, xsrc);
 	XFreePixmap(xdisplay, xmask);
 	XFlush(xdisplay);
+	drawqunlock();
 }
 
 void
 cursorarrow(void)
 {
+	drawqlock();
 	if(xcursor != 0){
 		XFreeCursor(xdisplay, xcursor);
 		xcursor = 0;
@@ -205,6 +213,7 @@
 	}
 	XUndefineCursor(xdisplay, xdrawable);
 	XFlush(xdisplay);
+	drawqunlock();
 }
 
 static void
@@ -229,7 +238,7 @@
 	for(;;) {
 		//XWindowEvent(xkmcon, xdrawable, mask, &event);
 		XNextEvent(xkmcon, &event);
-		xselect(&event);
+		xselect(&event, xkmcon);
 		xkeyboard(&event);
 		xmouse(&event);
 		xexpose(&event);
@@ -464,11 +473,11 @@
 		exit(0);
 	}
 
-    clipboard = XInternAtom(xkmcon, "CLIPBOARD", False);
-    utf8string = XInternAtom(xkmcon, "UTF8_STRING", False);
-    targets = XInternAtom(xkmcon, "TARGETS", False);
-    text = XInternAtom(xkmcon, "TEXT", False);
-    compoundtext = XInternAtom(xkmcon, "COMPOUND_TEXT", False);
+	clipboard = XInternAtom(xkmcon, "CLIPBOARD", False);
+	utf8string = XInternAtom(xkmcon, "UTF8_STRING", False);
+	targets = XInternAtom(xkmcon, "TARGETS", False);
+	text = XInternAtom(xkmcon, "TEXT", False);
+	compoundtext = XInternAtom(xkmcon, "COMPOUND_TEXT", False);
 
 	xblack = screen->black_pixel;
 	xwhite = screen->white_pixel;
@@ -625,13 +634,6 @@
 }
 
 static void
-xselection(XEvent *e)
-{
-	XSelectionRequestEvent *xre;
-	XSelectionEvent *xe;
-}
-
-static void
 xmapping(XEvent *e)
 {
 	XMappingEvent *xe;
@@ -691,7 +693,7 @@
 		return;
 
 
-	XLookupString(e,NULL,0,&k,NULL);
+	XLookupString((XKeyEvent*)e, NULL, 0, &k, NULL);
 
 	if(k == XK_Multi_key || k == NoSymbol)
 		return;
@@ -781,12 +783,14 @@
 		case XK_Super_R:
 		case XK_Hyper_L:
 		case XK_Hyper_R:
-            return;
+			return;
 		default:		/* not ISO-1 or tty control */
-  			if(k>0xff) {
-                k = keysym2ucs(k); /* supplied by X */
-                if(k == -1) return;
-           	}
+  			if(k>0xff){
+				k = keysym2ucs(k); /* supplied by X */
+				if(k == -1)
+					return;
+			}
+			break;
 		}
 	}
 
@@ -811,9 +815,27 @@
 	XButtonEvent *be;
 	XMotionEvent *me;
 
+	if(putsnarf != assertsnarf){
+		assertsnarf = putsnarf;
+		XSetSelectionOwner(xkmcon, XA_PRIMARY, xdrawable, CurrentTime);
+		if(clipboard != None)
+			XSetSelectionOwner(xkmcon, clipboard, xdrawable, CurrentTime);
+		XFlush(xkmcon);
+	}
+
 	switch(e->type){
 	case ButtonPress:
 		be = (XButtonEvent *)e;
+		/* 
+		 * Fake message, just sent to make us announce snarf.
+		 * Apparently state and button are 16 and 8 bits on
+		 * the wire, since they are truncated by the time they
+		 * get to us.
+		 */
+		if(be->send_event
+		&& (~be->state&0xFFFF)==0
+		&& (~be->button&0xFF)==0)
+			return;
 		ms.xy.x = be->x;
 		ms.xy.y = be->y;
 		s = be->state;
@@ -921,174 +943,226 @@
 	/* no-op */
 }
 
-typedef struct Clip	Clip;
+int
+atlocalconsole(void)
+{
+	char *p, *q;
+	char buf[128];
+
+	p = getenv("DRAWTERM_ATLOCALCONSOLE");
+	if(p && atoi(p) == 1)
+		return 1;
+
+	p = getenv("DISPLAY");
+	if(p == nil)
+		return 0;
+
+	/* extract host part */
+	q = strchr(p, ':');
+	if(q == nil)
+		return 0;
+	*q = 0;
+
+	if(strcmp(p, "") == 0)
+		return 1;
+
+	/* try to match against system name (i.e. for ssh) */
+	if(gethostname(buf, sizeof buf) == 0){
+		if(strcmp(p, buf) == 0)
+			return 1;
+		if(strncmp(p, buf, strlen(p)) == 0 && buf[strlen(p)]=='.')
+			return 1;
+	}
+
+	return 0;
+}
+
+/*
+ * Cut and paste.  Just couldn't stand to make this simple...
+ */
+
+typedef struct Clip Clip;
 struct Clip
 {
 	char buf[SnarfSize];
-	ulong n;
-	int want, have;
 	QLock lk;
-	Rendez vous;
 };
-
 Clip clip;
 
-enum {
-	Chunk = 2048
-};
+#undef long	/* sic */
+#undef ulong
 
-static void
-xselect(XEvent *e)
+static char*
+_xgetsnarf(Display *xd)
 {
-	XSelectionRequestEvent *q;
-	XEvent r;
-	Atom a[4];
-	char *name;
+	uchar *data, *xdata;
+	Atom clipboard, type, prop;
+	ulong len, lastlen, dummy;
+	int fmt, i;
+	Window w;
 
+	qlock(&clip.lk);
+	/*
+	 * Have we snarfed recently and the X server hasn't caught up?
+	 */
+	if(putsnarf != assertsnarf)
+		goto mine;
 
-	if(e->type != SelectionRequest)
-		return;
+	/*
+	 * Is there a primary selection (highlighted text in an xterm)?
+	 */
+	clipboard = XA_PRIMARY;
+	w = XGetSelectionOwner(xd, XA_PRIMARY);
+	if(w == xdrawable){
+	mine:
+		data = (uchar*)strdup(clip.buf);
+		goto out;
+	}
 
 	/*
-	 * The lock is around the whole routine because we use the 
-	 * lock to make sure two people aren't sending on xkmcon
-	 * at once.
+	 * If not, is there a clipboard selection?
 	 */
-	q = (XSelectionRequestEvent*)e;
+	if(w == None && clipboard != None){
+		clipboard = clipboard;
+		w = XGetSelectionOwner(xd, clipboard);
+		if(w == xdrawable)
+			goto mine;
+	}
 
-    r.xselection.property = q->property;
-    if(q->target == targets) {
-        a[0] = XA_STRING;
-        a[1] = utf8string;
-        a[2] = text;
-        a[3] = compoundtext;
-
-        XChangeProperty(xkmcon, q->requestor, q->property, q->target,
-            8, PropModeReplace, (uchar*)a, sizeof a);
-    }else if(q->target == XA_STRING || q->target == utf8string || q->target == text || q->target == compoundtext){
-		qlock(&clip.lk);
-		XChangeProperty(xkmcon, q->requestor, q->property, q->target, 8,
-			PropModeReplace, (uchar*)clip.buf, strlen(clip.buf));
-		qunlock(&clip.lk);
-	}else {
-        name = XGetAtomName(xkmcon, q->target);
-        if(strcmp(name, "TIMESTAMP") != 0)
-            fprint(2, "%s: cannot handle selection request for '%s' (%d)\n", argv0, name, (int)q->target);
-		r.xselection.property = None;
+	/*
+	 * If not, give up.
+	 */
+	if(w == None){
+		data = nil;
+		goto out;
 	}
 		
-	r.xselection.type = SelectionNotify;
-	r.xselection.display = q->display;
-	r.xselection.requestor = q->requestor;
-	r.xselection.selection = q->selection;
-	r.xselection.target = q->target;
-	r.xselection.time = q->time;
-	XSendEvent(xkmcon, q->requestor, False, 0, &r);
-	XFlush(xkmcon);
-}
-
-int
-haveclip(void *a)
-{
-	return clip.want == clip.have;
-}
-
-#undef long /* sic */
-char*
-clipread(void)
-{
-	Window w;
-	XEvent e;
-	Atom type;
-	unsigned long len, lleft, left, dummy;
-	int i, fmt, res;
-	uchar *data;
-
-	qlock(&clip.lk);
-	w = XGetSelectionOwner(xsnarfcon, XA_PRIMARY);
-	if(w == xdrawable)
-		data = (uchar*)strdup(clip.buf);
-	else if(w == None)
+	/*
+	 * We should be waiting for SelectionNotify here, but it might never
+	 * come, and we have no way to time out.  Instead, we will clear
+	 * local property #1, request our buddy to fill it in for us, and poll
+	 * until he's done or we get tired of waiting.
+	 *
+	 * We should try to go for utf8string instead of XA_STRING,
+	 * but that would add to the polling.
+	 */
+	prop = 1;
+	XChangeProperty(xd, xdrawable, prop, XA_STRING, 8, PropModeReplace, (uchar*)"", 0);
+	XConvertSelection(xd, clipboard, XA_STRING, prop, xdrawable, CurrentTime);
+	XFlush(xd);
+	lastlen = 0;
+	for(i=0; i<10 || (lastlen!=0 && i<30); i++){
+		usleep(100*1000);
+		XGetWindowProperty(xd, xdrawable, prop, 0, 0, 0, AnyPropertyType,
+			&type, &fmt, &dummy, &len, &data);
+		if(lastlen == len && len > 0)
+			break;
+		lastlen = len;
+	}
+	if(i == 10){
 		data = nil;
-	else {	
-		/*
-		 * we're supposed to get a notification, but we seem not to,
-		 * so let's just watch and see when the buffer stabilizes.
-		 * if you know how to fix this, mail rsc@plan9.bell-labs.com.
-		 */
-		XChangeProperty(xsnarfcon, xdrawable, XA_PRIMARY, XA_STRING, 8, PropModeReplace,
-		 	(uchar*)"", 0);
-		XConvertSelection(xsnarfcon, XA_PRIMARY, XA_STRING, None, xdrawable, CurrentTime);
-		XFlush(xsnarfcon);
-		for(i=0; i<30; i++){
-		 	osmsleep(100);
-			XGetWindowProperty(xsnarfcon, xdrawable, XA_STRING, 0, 0, 0, AnyPropertyType,
-				&type, &fmt, &len, &left, &data);
-			if(lleft == left && left > 0)
-				break;
-			lleft = left;
-		}
-		if(left > 0){
-			res = XGetWindowProperty(xsnarfcon, xdrawable, XA_STRING, 0, left, 0, 
-				AnyPropertyType, &type, &fmt, &len, &dummy, &data);
-			data = (uchar*)strdup(data);
+		goto out;
+	}
+	/* get the property */
+	data = nil;
+	XGetWindowProperty(xd, xdrawable, prop, 0, SnarfSize/sizeof(ulong), 0, 
+		AnyPropertyType, &type, &fmt, &len, &dummy, &xdata);
+	if((type != XA_STRING && type != utf8string) || len == 0){
+		if(xdata)
+			XFree(xdata);
+		data = nil;
+	}else{
+		if(xdata){
+			data = (uchar*)strdup((char*)xdata);
+			XFree(xdata);
 		}else
 			data = nil;
 	}
+out:
 	qunlock(&clip.lk);
 	return (char*)data;
 }
 
-int
-clipwrite(char *buf)
+static void
+_xputsnarf(Display *xd, char *data)
 {
-	int n;
+	XButtonEvent e;
 
-	n = strlen(buf);
+	if(strlen(data) >= SnarfSize)
+		return;
 	qlock(&clip.lk);
-	if(n >= SnarfSize)
-		n = SnarfSize - 1;
-	memmove(clip.buf, buf, n);
-	clip.buf[n] = 0;
-	clip.n = n;
-	/*
-	 * xkmcon so that we get the event in the select loop.  
-	 * It seems to be okay to send a message and read an event
-	 * from a Display* at the same time.  Let's hope so.
-	 */
-	XSetSelectionOwner(xkmcon, XA_PRIMARY, xdrawable, CurrentTime);
-	XFlush(xkmcon);
+	strcpy(clip.buf, data);
+
+	/* leave note for mouse proc to assert selection ownership */
+	putsnarf++;
+
+	/* send mouse a fake event so snarf is announced */
+	memset(&e, 0, sizeof e);
+	e.type = ButtonPress;
+	e.window = xdrawable;
+	e.state = ~0;
+	e.button = ~0;
+	XSendEvent(xd, xdrawable, True, ButtonPressMask, (XEvent*)&e);
+	XFlush(xd);
 	qunlock(&clip.lk);
-	return n;
 }
-#define long int /* sic */
 
-int
-atlocalconsole(void)
+static void
+xselect(XEvent *e, Display *xd)
 {
-	char *p, *q;
-	char buf[128];
+	char *name;
+	XEvent r;
+	XSelectionRequestEvent *xe;
+	Atom a[4];
 
-	p = getenv("DISPLAY");
-	if(p == nil)
-		return 0;
+	memset(&r, 0, sizeof r);
+	xe = (XSelectionRequestEvent*)e;
+if(0) fprint(2, "xselect target=%d requestor=%d property=%d selection=%d\n",
+	xe->target, xe->requestor, xe->property, xe->selection);
+	r.xselection.property = xe->property;
+	if(xe->target == targets){
+		a[0] = XA_STRING;
+		a[1] = utf8string;
+		a[2] = text;
+		a[3] = compoundtext;
 
-	/* extract host part */
-	q = strchr(p, ':');
-	if(q == nil)
-		return 0;
-	*q = 0;
+		XChangeProperty(xd, xe->requestor, xe->property, xe->target,
+			8, PropModeReplace, (uchar*)a, sizeof a);
+	}else if(xe->target == XA_STRING || xe->target == utf8string || xe->target == text || xe->target == compoundtext){
+		/* if the target is STRING we're supposed to reply with Latin1 XXX */
+		qlock(&clip.lk);
+		XChangeProperty(xd, xe->requestor, xe->property, xe->target,
+			8, PropModeReplace, (uchar*)clip.buf, strlen(clip.buf));
+		qunlock(&clip.lk);
+	}else{
+		name = XGetAtomName(xd, xe->target);
+		if(strcmp(name, "TIMESTAMP") != 0)
+			fprint(2, "%s: cannot handle selection request for '%s' (%d)\n", argv0, name, (int)xe->target);
+		r.xselection.property = None;
+	}
 
-	if(strcmp(p, "") == 0)
-		return 1;
+	r.xselection.display = xe->display;
+	/* r.xselection.property filled above */
+	r.xselection.target = xe->target;
+	r.xselection.type = SelectionNotify;
+	r.xselection.requestor = xe->requestor;
+	r.xselection.time = xe->time;
+	r.xselection.send_event = True;
+	r.xselection.selection = xe->selection;
+	XSendEvent(xd, xe->requestor, False, 0, &r);
+	XFlush(xd);
+}
 
-	/* try to match against system name (i.e. for ssh) */
-	if(gethostname(buf, sizeof buf) == 0){
-		if(strcmp(p, buf) == 0)
-			return 1;
-		if(strncmp(p, buf, strlen(p)) == 0 && buf[strlen(p)]=='.')
-			return 1;
-	}
-	
+char*
+clipread(void)
+{
+	return _xgetsnarf(xsnarfcon);
+}
+
+int
+clipwrite(char *buf)
+{
+	_xputsnarf(xsnarfcon, buf);
 	return 0;
 }
+
--- a/kern/devdraw.c
+++ b/kern/devdraw.c
@@ -183,6 +183,24 @@
 static	char Enamed[] = 	"image already has name";
 static	char Ewrongname[] = 	"wrong name for image";
 
+int
+drawcanqlock(void)
+{
+	return canqlock(&sdraw.lk);
+}
+
+void
+drawqlock(void)
+{
+	qlock(&sdraw.lk);
+}
+
+void
+drawqunlock(void)
+{
+	qunlock(&sdraw.lk);
+}
+
 static int
 drawgen(Chan *c, char *name, Dirtab *dt, int ndt, int s, Dir *dp)
 {
--- a/kern/fns.h
+++ b/kern/fns.h
@@ -79,6 +79,9 @@
 int		devwstat(Chan*, uchar*, int);
 void		drawactive(int);
 void		drawcmap(void);
+int		drawcanqlock(void);
+void		drawqlock(void);
+void		drawqunlock(void);
 void		dumpaproc(Proc*);
 void		dumpqueues(void);
 void		dumpregs(Ureg*);