[comp.soft-sys.andrew] ICCCM non-compliance bug, and work-around for XView bug

guy@auspex.auspex.com (Guy Harris) (09/30/90)

1) Andrew doesn't handle the INCR property mechanism, used for handling
   large selections, properly.  The value of the INCR property, which is
   supposed to be a lower bound on the number of characters in the
   selection, is *NOT* supposed to be an ASCII string for that number,
   it's supposed to be the number itself (yes, this is supposed to work
   even between machines with different byte orders; the X server knows
   the byte order of all its clients and, since properties are composed
   of items all the same number of bytes in size, it can translate
   properties to the appropriate byte order).

   Without this fix, sucking "/etc/termcap" into, for example, "ez",
   selecting the entire file (assuming you have a normal humongous
   "/etc/termcap" file), and trying to paste it into, for example,
   "xedit", won't work, nor will sucking it into "xedit", selecting it
   all, and trying to paste it into "ez".

2) XView incorrectly gives the TARGETS target the type TARGETS rather
   than ATOM.  A bug report has been sent to Sun, along with a fix (the
   fix is pretty straightforward); however, not everybody with XView
   necessarily has source or necessarily wants to run a version they've
   built from source, and the Open Windows 2.0 distribution has the bug,
   so it's probably polite to have Andrew compensate for XView's
   deficiencies here.

3) Speaking of XView, it insists that the LENGTH target be supported.
   The ICCCM doesn't say what its value should be; it's supposed to be
   the "number of bytes in (the) selection", but that number can vary
   depending on which target is requested; XView seems to want it to be
   the number of bytes in the STRING target, so that's what we give it.

4) Whilst we're at it, we have the code that handles selections issue
   complaints in some situations, rather than silently ignoring the
   selection requests; had it done so in the first place, I would have
   spent less time trying to figure out why I couldn't cut and paste
   between "ez" and "textedit"....

*** atk/basics/x/xim.c.dist	Thu Sep 27 16:54:07 1990
--- atk/basics/x/xim.c	Thu Sep 27 17:03:00 1990
***************
*** 194,199 ****
--- 194,200 ----
  static Atom xim_TEXT;
  static Atom xim_INCR;
  static Atom xim_CLIPBOARD;
+ static Atom xim_LENGTH;
  
  static long LeftIncrement, TopIncrement, CurrLeftIncr, CurrTopIncr;
  
***************
*** 762,767 ****
--- 763,769 ----
  	xim_TEXT = XInternAtom(xDisplay, "TEXT", FALSE);
  	xim_INCR = XInternAtom(xDisplay, "INCR", FALSE);
  	xim_CLIPBOARD = XInternAtom(xDisplay, "CLIPBOARD", FALSE);
+ 	xim_LENGTH = XInternAtom(xDisplay, "LENGTH", FALSE);
          }
          for (i=0;i<8;i++) {
  	    Atom RetAtom;
***************
*** 2868,2880 ****
  			&actualtype, &actualformat,
  			&nitems, &remainingbytes, &propList);
  		target = None;
! 		if (success == Success && actualtype == XA_ATOM) 
! 		     for (i = 0; DesirableTargets[i] != None && target == None; i++)
! 			for (j = 0; j < nitems && target == None; j++)
  				if (DesirableTargets[i] == ((Atom *)propList)[j]) 
  					target = DesirableTargets[i];
  		XFree((caddr_t)propList);
! 		if (target == None) break;
  
  		/* okay, we'll ask for the selection in type 'target' */
  
--- 2870,2898 ----
  			&actualtype, &actualformat,
  			&nitems, &remainingbytes, &propList);
  		target = None;
! 		if (success == Success) {
! 		    /*
! 		     * XXX - work around XView bug, wherein it gives the type
! 		     * TARGETS rather than ATOM to the result.
! 		     */
! 		    if (actualtype == xim_TARGETS)
! 			actualtype = XA_ATOM;
! 		    if (actualtype == XA_ATOM) {
! 			for (i = 0; DesirableTargets[i] != None && target == None; i++)
! 			    for (j = 0; j < nitems && target == None; j++)
  				if (DesirableTargets[i] == ((Atom *)propList)[j]) 
  					target = DesirableTargets[i];
+ 		    } else
+ 			fprintf(stderr,"TARGETS targets isn't of type ATOM\n");
+ 		}
  		XFree((caddr_t)propList);
! 		if (target == None) {
! 		    /*
! 		     * They don't speak our language.
! 		     */
! 		    fprintf(stderr,"Owner of selection handles neither ATK nor STRING nor TEXT\n");
! 		    break;
! 		}
  
  		/* okay, we'll ask for the selection in type 'target' */
  
***************
*** 2898,2904 ****
  		/* is is an INCR ? */
  		if (actualtype == xim_INCR) {
  			/* nuts, it is INCR.  start multiple retrievals */
! 			remainingbytes = atoi(elt->string->string);
  			if (*elt->xfree)  XFree(elt->string->string);
  			else   free(elt->string->string);
  			*elt->xfree = FALSE;
--- 2916,2922 ----
  		/* is is an INCR ? */
  		if (actualtype == xim_INCR) {
  			/* nuts, it is INCR.  start multiple retrievals */
! 			remainingbytes = *(long *)elt->string->string;
  			if (*elt->xfree)  XFree(elt->string->string);
  			else   free(elt->string->string);
  			*elt->xfree = FALSE;
***************
*** 3057,3065 ****
  ProcessRequest(req)
  	XSelectionRequestEvent *req;
  {
! 	Atom targets[6];
  	struct expandstring cutBuffer;
- 	char m[12];
  	boolean xfree;
  	struct proplistelt *elt;
  	Atom actualtype;
--- 3075,3082 ----
  ProcessRequest(req)
  	XSelectionRequestEvent *req;
  {
! 	Atom targets[7];
  	struct expandstring cutBuffer;
  	boolean xfree;
  	struct proplistelt *elt;
  	Atom actualtype;
***************
*** 3075,3082 ****
  		targets[3] = xim_TEXT;
  		targets[4] = xim_TARGETS;
  		targets[5] = xim_MULTIPLE;
  		XChangeProperty(req->display, req->requestor, req->property,
! 			XA_ATOM, 32, PropModeReplace, (char *)targets, 6);
  	}
  	else if (req->target == xim_TIMESTAMP) {
  		XChangeProperty(req->display, req->requestor, req->property,
--- 3092,3100 ----
  		targets[3] = xim_TEXT;
  		targets[4] = xim_TARGETS;
  		targets[5] = xim_MULTIPLE;
+ 		targets[6] = xim_LENGTH;
  		XChangeProperty(req->display, req->requestor, req->property,
! 			XA_ATOM, 32, PropModeReplace, (char *)targets, 7);
  	}
  	else if (req->target == xim_TIMESTAMP) {
  		XChangeProperty(req->display, req->requestor, req->property,
***************
*** 3084,3090 ****
  			(char *)&SelectionStartTime, 1);
  	}
  	else if (req->target == xim_ATK  ||  req->target == XA_STRING  
! 			||  req->target == xim_TEXT) {
  		if ( ! IOwnSelection ||
  				(req->time != CurrentTime  
  				&&  0 > (long)(req->time - SelectionStartTime))) {
--- 3102,3109 ----
  			(char *)&SelectionStartTime, 1);
  	}
  	else if (req->target == xim_ATK  ||  req->target == XA_STRING  
! 			||  req->target == xim_TEXT
! 			||  req->target == xim_LENGTH) {
  		if ( ! IOwnSelection ||
  				(req->time != CurrentTime  
  				&&  0 > (long)(req->time - SelectionStartTime))) {
***************
*** 3103,3142 ****
  			req->property = None;
  			return FALSE;
  		}
! 		if (req->target == XA_STRING  ||  req->target == xim_TEXT) {
! 			/* delete objects and remove styles */
  			Unscribe(&cutBuffer);
- 			if (req->target == xim_TEXT)
- 				req->target = XA_STRING;
- 		}
- 
- 		/* send the data to the requestor */
- 		if (cutBuffer.length < MAXXFER) {
  			/* send it with one XChangeProperty */
! 			XChangeProperty(req->display, req->requestor, req->property,
! 				req->target, 8, PropModeReplace, 
! 				cutBuffer.string, cutBuffer.length);
  			if (xfree)  XFree(cutBuffer.string);
  			else   free(cutBuffer.string);
! 		}
! 		else {
! 			/* send it via INCR scheme */
! 			SetPNMask(req->display, req->requestor);
! 			elt = PostPropList(req->requestor, req->property, 
! 				prop_OutIncr, req->time);
! 			elt->string = (struct expandstring *)
! 				malloc(sizeof(struct expandstring));
! 			*(elt->string) = cutBuffer;
! 			elt->xfree = (xfree) ? &xfree : NULL;
! 				/* the value of elt->xfree is tested
! 				for non-NULL, but *elt->xfree is not used */
! 			elt->type = req->target;
! 			sprintf(m, "%d", elt->string->length);
! 			XChangeProperty(req->display, req->requestor, req->property,
! 				xim_INCR, 8, PropModeReplace, 
! 				m, strlen(m));
! 			/* the main Interact loop will handle PropertyEvents 
! 				we have  to exit to do the SelectionNotify */
  		}
  		if (OriginalNerrors != Nerrors) {
  			/* OOPS.  There was an error.  Reject the request. */
--- 3122,3184 ----
  			req->property = None;
  			return FALSE;
  		}
! 		if (req->target == xim_LENGTH) {
! 			/*
! 			 * Keep XView happy by telling it how big the
! 			 * selection is.  The SunView System Programmer's
! 			 * Guide says that SELN_REQ_BYTESIZE is "the number
! 			 * of bytes in the selection's ascii(sic) contents",
! 			 * and that's what XView turns into a request for
! 			 * LENGTH, so we delete objects and remove styles,
! 			 * and give them the resulting size.
! 			 */
  			Unscribe(&cutBuffer);
  			/* send it with one XChangeProperty */
! 			XChangeProperty(req->display, req->requestor,
! 				req->property, req->target, 32,
! 				PropModeReplace,
! 				(unsigned char *)&cutBuffer.length, 1);
  			if (xfree)  XFree(cutBuffer.string);
  			else   free(cutBuffer.string);
! 		} else {
! 			if (req->target == XA_STRING  ||  req->target == xim_TEXT) {
! 				/* delete objects and remove styles */
! 				Unscribe(&cutBuffer);
! 				if (req->target == xim_TEXT)
! 					req->target = XA_STRING;
! 			}
! 
! 			/* send the data to the requestor */
! 			if (cutBuffer.length < MAXXFER) {
! 				/* send it with one XChangeProperty */
! 				XChangeProperty(req->display, req->requestor,
! 					req->property, req->target, 8,
! 					PropModeReplace, cutBuffer.string,
! 					cutBuffer.length);
! 				if (xfree)  XFree(cutBuffer.string);
! 				else   free(cutBuffer.string);
! 			}
! 			else {
! 				/* send it via INCR scheme */
! 				SetPNMask(req->display, req->requestor);
! 				elt = PostPropList(req->requestor,
! 				req->property, prop_OutIncr, req->time);
! 				elt->string = (struct expandstring *)
! 					malloc(sizeof(struct expandstring));
! 				*(elt->string) = cutBuffer;
! 				elt->xfree = (xfree) ? &xfree : NULL;
! 					/* the value of elt->xfree is tested
! 					for non-NULL, but *elt->xfree is not used */
! 				elt->type = req->target;
! 				XChangeProperty(req->display, req->requestor,
! 					req->property,xim_INCR, 32,
! 					PropModeReplace,
! 					(unsigned char *)&elt->string->length,
! 					1);
! 				/* the main Interact loop will handle
! 				 PropertyEvents we have  to exit to do the
! 				 SelectionNotify */
! 			}
  		}
  		if (OriginalNerrors != Nerrors) {
  			/* OOPS.  There was an error.  Reject the request. */