xref: /plan9/sys/src/cmd/unix/drawterm/gui-x11/x11.c (revision 58da3067adcdccaaa043d0bfde28ba83b7ced07d)
1 #include "u.h"
2 #include "lib.h"
3 #include "dat.h"
4 #include "fns.h"
5 #include "error.h"
6 
7 #include <draw.h>
8 #include <memdraw.h>
9 #include <keyboard.h>
10 #include <cursor.h>
11 #include "screen.h"
12 
13 #define argv0 "drawterm"
14 
15 typedef struct Cursor Cursor;
16 
17 #undef	long
18 #define	Font		XFont
19 #define	Screen	XScreen
20 #define	Display	XDisplay
21 #define	Cursor	XCursor
22 
23 #include <X11/Xlib.h>
24 #include <X11/Xatom.h>
25 #include <X11/Xutil.h>
26 #include <X11/IntrinsicP.h>
27 #include <X11/StringDefs.h>
28 #include <X11/keysym.h>
29 #include "keysym2ucs.h"
30 
31 #undef	Font
32 #undef	Screen
33 #undef	Display
34 #undef	Cursor
35 #define	long	int
36 
37 /* perfect approximation to NTSC = .299r+.587g+.114b when 0 ≤ r,g,b < 256 */
38 #define RGB2K(r,g,b)	((156763*(r)+307758*(g)+59769*(b))>>19)
39 
40 enum
41 {
42 	PMundef	= ~0		/* undefined pixmap id */
43 };
44 
45 /*
46  * Structure pointed to by X field of Memimage
47  */
48 typedef struct Xmem Xmem;
49 struct Xmem
50 {
51 	int	pmid;	/* pixmap id for screen ldepth instance */
52 	XImage *xi;	/* local image if we currenty have the data */
53 	int	dirty;
54 	Rectangle dirtyr;
55 	Rectangle r;
56 	uintptr pc;	/* who wrote into xi */
57 };
58 
59 static int	xgcfillcolor;
60 static int	xgcfillcolor0;
61 static int	xgcsimplecolor0;
62 static int	xgcsimplepm0;
63 
64 static	XDisplay*	xdisplay;	/* used holding draw lock */
65 static int				xtblbit;
66 static int 			plan9tox11[256]; /* Values for mapping between */
67 static int 			x11toplan9[256]; /* X11 and Plan 9 */
68 static	GC		xgcfill, xgccopy, xgcsimplesrc, xgczero, xgcreplsrc;
69 static	GC		xgcfill0, xgccopy0, xgcsimplesrc0, xgczero0, xgcreplsrc0;
70 static	ulong	xscreenchan;
71 static	Drawable	xscreenid;
72 static	Visual		*xvis;
73 
74 static int xdraw(Memdrawparam*);
75 
76 #define glenda_width 48
77 #define glenda_height 48
78 static unsigned short glenda_bits[] = {
79    0xffff, 0xffff, 0xffff, 0xffff, 0xffe9, 0xffff, 0x7fff, 0xffae, 0xffff,
80    0xffff, 0xffbe, 0xffff, 0x1fff, 0xff3f, 0xffff, 0xbfff, 0xfe6e, 0xffff,
81    0xbbff, 0xfcce, 0xffff, 0xffff, 0xf98c, 0xffff, 0xe5ff, 0xf31b, 0xffff,
82    0x87ff, 0xe617, 0xffff, 0x05ff, 0xdf37, 0xffff, 0x0fff, 0x7ffe, 0xffff,
83    0x1bff, 0xfffc, 0xfffa, 0x37ff, 0xfffc, 0xfffb, 0xd7ff, 0xfffc, 0xfff7,
84    0xcfff, 0xffff, 0xfff7, 0xcfff, 0xffff, 0xffef, 0xdfff, 0xffff, 0xffef,
85    0xafff, 0xffff, 0xffdf, 0xefff, 0xffff, 0xfff3, 0xdfff, 0xefff, 0xffd3,
86    0xdfff, 0xc7ff, 0xffdf, 0xefff, 0xefff, 0xffef, 0xcfff, 0xffff, 0xffcf,
87    0xdfff, 0xffff, 0xffd9, 0x9fff, 0x7fff, 0xffd0, 0xbfff, 0xffff, 0xffd7,
88    0x7fff, 0xbfff, 0xffd0, 0x3fff, 0x3fff, 0xffd9, 0x7fff, 0x3fff, 0xffcb,
89    0x3fff, 0xffff, 0xffdc, 0x3fff, 0xffff, 0xffdf, 0x3fff, 0xffff, 0xff9f,
90    0x3fff, 0xffff, 0xffdf, 0x8fff, 0xffff, 0xff9f, 0xa7ff, 0xffff, 0xffdf,
91    0xe3ff, 0xffff, 0xffcf, 0xe9ff, 0xffff, 0xffcf, 0xf1ff, 0xffff, 0xffef,
92    0xf3ff, 0xffff, 0xffe7, 0xf9ff, 0xffff, 0xffe7, 0x53ff, 0xffff, 0xffe1,
93    0x07ff, 0x7ffc, 0xffc6, 0x17ff, 0xeff0, 0xffee, 0xffff, 0xc781, 0xffe5,
94    0xffff, 0x8807, 0xffe0, 0xffff, 0x003f, 0xfff0, 0xffff, 0x1fff, 0xfffe
95 };
96 
97 /*
98  * Synchronize images between X bitmaps and in-memory bitmaps.
99  */
100 static void
addrect(Rectangle * rp,Rectangle r)101 addrect(Rectangle *rp, Rectangle r)
102 {
103 	if(rp->min.x >= rp->max.x)
104 		*rp = r;
105 	else
106 		combinerect(rp, r);
107 }
108 
109 static XImage*
getXdata(Memimage * m,Rectangle r)110 getXdata(Memimage *m, Rectangle r)
111 {
112 	uchar *p;
113 	int x, y;
114 	Xmem *xm;
115 	Point xdelta, delta;
116 	Point tp;
117 
118  	xm = m->X;
119  	if(xm == nil)
120  		return nil;
121 
122 	assert(xm != nil && xm->xi != nil);
123 
124  	if(xm->dirty == 0)
125  		return xm->xi;
126 
127  	r = xm->dirtyr;
128 	if(Dx(r)==0 || Dy(r)==0)
129 		return xm->xi;
130 
131 	delta = subpt(r.min, m->r.min);
132 	tp = xm->r.min;	/* avoid unaligned access on digital unix */
133 	xdelta = subpt(r.min, tp);
134 
135 	XGetSubImage(xdisplay, xm->pmid, delta.x, delta.y, Dx(r), Dy(r),
136 		AllPlanes, ZPixmap, xm->xi, xdelta.x, xdelta.y);
137 
138 	if(xtblbit && m->chan == CMAP8)
139 		for(y=r.min.y; y<r.max.y; y++)
140 			for(x=r.min.x, p=byteaddr(m, Pt(x,y)); x<r.max.x; x++, p++)
141 				*p = x11toplan9[*p];
142 
143 	xm->dirty = 0;
144 	xm->dirtyr = Rect(0,0,0,0);
145 	return xm->xi;
146 }
147 
148 static void
putXdata(Memimage * m,Rectangle r)149 putXdata(Memimage *m, Rectangle r)
150 {
151 	Xmem *xm;
152 	XImage *xi;
153 	GC g;
154 	Point xdelta, delta;
155 	Point tp;
156 	int x, y;
157 	uchar *p;
158 
159 	xm = m->X;
160 	if(xm == nil)
161 		return;
162 
163 	assert(xm != nil);
164 	assert(xm->xi != nil);
165 
166 	xi = xm->xi;
167 
168 	g = (m->chan == GREY1) ? xgccopy0 : xgccopy;
169 
170 	delta = subpt(r.min, m->r.min);
171 	tp = xm->r.min;	/* avoid unaligned access on digital unix */
172 	xdelta = subpt(r.min, tp);
173 
174 	if(xtblbit && m->chan == CMAP8)
175 		for(y=r.min.y; y<r.max.y; y++)
176 			for(x=r.min.x, p=byteaddr(m, Pt(x,y)); x<r.max.x; x++, p++)
177 				*p = plan9tox11[*p];
178 
179 	XPutImage(xdisplay, xm->pmid, g, xi, xdelta.x, xdelta.y, delta.x, delta.y, Dx(r), Dy(r));
180 
181 	if(xtblbit && m->chan == CMAP8)
182 		for(y=r.min.y; y<r.max.y; y++)
183 			for(x=r.min.x, p=byteaddr(m, Pt(x,y)); x<r.max.x; x++, p++)
184 				*p = x11toplan9[*p];
185 }
186 
187 static void
dirtyXdata(Memimage * m,Rectangle r)188 dirtyXdata(Memimage *m, Rectangle r)
189 {
190 	Xmem *xm;
191 
192 	if((xm = m->X) != nil){
193 		xm->dirty = 1;
194 		addrect(&xm->dirtyr, r);
195 	}
196 }
197 
198 Memimage*
xallocmemimage(Rectangle r,ulong chan,int pmid)199 xallocmemimage(Rectangle r, ulong chan, int pmid)
200 {
201 	Memimage *m;
202 	Xmem *xm;
203 	XImage *xi;
204 	int offset;
205 	int d;
206 
207 	m = _allocmemimage(r, chan);
208 	if(m == nil)
209 		return nil;
210 	if(chan != GREY1 && chan != xscreenchan)
211 		return m;
212 
213 	d = m->depth;
214 	xm = mallocz(sizeof(Xmem), 1);
215 	if(pmid != PMundef)
216 		xm->pmid = pmid;
217 	else
218 		xm->pmid = XCreatePixmap(xdisplay, xscreenid, Dx(r), Dy(r), (d==32) ? 24 : d);
219 
220 	if(m->depth == 24)
221 		offset = r.min.x&(4-1);
222 	else
223 		offset = r.min.x&(31/m->depth);
224 	r.min.x -= offset;
225 
226 	assert(wordsperline(r, m->depth) <= m->width);
227 
228 	xi = XCreateImage(xdisplay, xvis, m->depth==32?24:m->depth, ZPixmap, 0,
229 		(char*)m->data->bdata, Dx(r), Dy(r), 32, m->width*sizeof(ulong));
230 
231 	if(xi == nil){
232 		_freememimage(m);
233 		return nil;
234 	}
235 
236 	xm->xi = xi;
237 	xm->pc = getcallerpc(&r);
238 	xm->r = r;
239 
240 	/*
241 	 * Set the parameters of the XImage so its memory looks exactly like a
242 	 * Memimage, so we can call _memimagedraw on the same data.  All frame
243 	 * buffers we've seen, and Plan 9's graphics code, require big-endian
244 	 * bits within bytes, but little endian byte order within pixels.
245 	 */
246 	xi->bitmap_unit = m->depth < 8 || m->depth == 24 ? 8 : m->depth;
247 	xi->byte_order = LSBFirst;
248 	xi->bitmap_bit_order = MSBFirst;
249 	xi->bitmap_pad = 32;
250 	xm->r = Rect(0,0,0,0);
251 	XInitImage(xi);
252 	XFlush(xdisplay);
253 
254 	m->X = xm;
255 	return m;
256 }
257 
258 void
xfillcolor(Memimage * m,Rectangle r,ulong v)259 xfillcolor(Memimage *m, Rectangle r, ulong v)
260 {
261 	GC gc;
262 	Xmem *dxm;
263 
264 	dxm = m->X;
265 	assert(dxm != nil);
266 	r = rectsubpt(r, m->r.min);
267 
268 	if(m->chan == GREY1){
269 		gc = xgcfill0;
270 		if(xgcfillcolor0 != v){
271 			XSetForeground(xdisplay, gc, v);
272 			xgcfillcolor0 = v;
273 		}
274 	}else{
275 		if(m->chan == CMAP8 && xtblbit)
276 			v = plan9tox11[v];
277 
278 		gc = xgcfill;
279 		if(xgcfillcolor != v){
280 			XSetForeground(xdisplay, gc, v);
281 			xgcfillcolor = v;
282 		}
283 	}
284 	XFillRectangle(xdisplay, dxm->pmid, gc, r.min.x, r.min.y, Dx(r), Dy(r));
285 }
286 
287 /*
288  * Replacements for libmemdraw routines.
289  * (They've been underscored.)
290  */
291 Memimage*
allocmemimage(Rectangle r,ulong chan)292 allocmemimage(Rectangle r, ulong chan)
293 {
294 	return xallocmemimage(r, chan, PMundef);
295 }
296 
297 void
freememimage(Memimage * m)298 freememimage(Memimage *m)
299 {
300 	Xmem *xm;
301 
302 	if(m == nil)
303 		return;
304 
305 	if(m->data->ref == 1){
306 		if((xm = m->X) != nil){
307 			if(xm->xi){
308 				xm->xi->data = nil;
309 				XFree(xm->xi);
310 			}
311 			XFreePixmap(xdisplay, xm->pmid);
312 			free(xm);
313 			m->X = nil;
314 		}
315 	}
316 	_freememimage(m);
317 }
318 
319 void
memfillcolor(Memimage * m,ulong val)320 memfillcolor(Memimage *m, ulong val)
321 {
322 	_memfillcolor(m, val);
323 	if(m->X){
324 		if((val & 0xFF) == 0xFF)
325 			xfillcolor(m, m->r, _rgbatoimg(m, val));
326 		else
327 			putXdata(m, m->r);
328 	}
329 }
330 
331 int
loadmemimage(Memimage * i,Rectangle r,uchar * data,int ndata)332 loadmemimage(Memimage *i, Rectangle r, uchar *data, int ndata)
333 {
334 	int n;
335 
336 	n = _loadmemimage(i, r, data, ndata);
337 	if(n > 0 && i->X)
338 		putXdata(i, r);
339 	return n;
340 }
341 
342 int
cloadmemimage(Memimage * i,Rectangle r,uchar * data,int ndata)343 cloadmemimage(Memimage *i, Rectangle r, uchar *data, int ndata)
344 {
345 	int n;
346 
347 	n = _cloadmemimage(i, r, data, ndata);
348 	if(n > 0 && i->X)
349 		putXdata(i, r);
350 	return n;
351 }
352 
353 ulong
pixelbits(Memimage * m,Point p)354 pixelbits(Memimage *m, Point p)
355 {
356 	if(m->X)
357 		getXdata(m, Rect(p.x, p.y, p.x+1, p.y+1));
358 	return _pixelbits(m, p);
359 }
360 
361 void
memimageinit(void)362 memimageinit(void)
363 {
364 	static int didinit = 0;
365 
366 	if(didinit)
367 		return;
368 
369 	didinit = 1;
370 	_memimageinit();
371 
372 	xfillcolor(memblack, memblack->r, 0);
373 	xfillcolor(memwhite, memwhite->r, 1);
374 }
375 
376 void
memimagedraw(Memimage * dst,Rectangle r,Memimage * src,Point sp,Memimage * mask,Point mp,int op)377 memimagedraw(Memimage *dst, Rectangle r, Memimage *src, Point sp, Memimage *mask, Point mp, int op)
378 {
379 	Memdrawparam *par;
380 
381 	if((par = _memimagedrawsetup(dst, r, src, sp, mask, mp, op)) == nil)
382 		return;
383 	_memimagedraw(par);
384 	if(!xdraw(par))
385 		putXdata(dst, par->r);
386 }
387 
388 static int
xdraw(Memdrawparam * par)389 xdraw(Memdrawparam *par)
390 {
391 	int dy, dx;
392 	unsigned m;
393 	Memimage *src, *dst, *mask;
394 	Xmem *dxm, *sxm, *mxm;
395 	GC gc;
396 	Rectangle r, sr, mr;
397 	ulong sdval;
398 
399 	dx = Dx(par->r);
400 	dy = Dy(par->r);
401 	src = par->src;
402 	dst = par->dst;
403 	mask = par->mask;
404 	r = par->r;
405 	sr = par->sr;
406 	mr = par->mr;
407 	sdval = par->sdval;
408 
409 	/*
410 	 * drawterm was distributed for years with
411 	 * "return 0;" right here.
412 	 * maybe we should give up on all this?
413 	 */
414 
415 	if((dxm = dst->X) == nil)
416 		return 0;
417 
418 	/*
419 	 * If we have an opaque mask and source is one opaque pixel we can convert to the
420 	 * destination format and just XFillRectangle.
421 	 */
422 	m = Simplesrc|Simplemask|Fullmask;
423 	if((par->state&m)==m){
424 		xfillcolor(dst, r, sdval);
425 		dirtyXdata(dst, par->r);
426 		return 1;
427 	}
428 
429 	/*
430 	 * If no source alpha, an opaque mask, we can just copy the
431 	 * source onto the destination.  If the channels are the same and
432 	 * the source is not replicated, XCopyArea suffices.
433 	 */
434 	m = Simplemask|Fullmask;
435 	if((par->state&(m|Replsrc))==m && src->chan == dst->chan && src->X){
436 		sxm = src->X;
437 		r = rectsubpt(r, dst->r.min);
438 		sr = rectsubpt(sr, src->r.min);
439 		if(dst->chan == GREY1)
440 			gc = xgccopy0;
441 		else
442 			gc = xgccopy;
443 		XCopyArea(xdisplay, sxm->pmid, dxm->pmid, gc,
444 			sr.min.x, sr.min.y, dx, dy, r.min.x, r.min.y);
445 		dirtyXdata(dst, par->r);
446 		return 1;
447 	}
448 
449 	/*
450 	 * If no source alpha, a 1-bit mask, and a simple source
451 	 * we can just copy through the mask onto the destination.
452 	 */
453 	if(dst->X && mask->X && !(mask->flags&Frepl)
454 	&& mask->chan == GREY1 && (par->state&Simplesrc)){
455 		Point p;
456 
457 		mxm = mask->X;
458 		r = rectsubpt(r, dst->r.min);
459 		mr = rectsubpt(mr, mask->r.min);
460 		p = subpt(r.min, mr.min);
461 		if(dst->chan == GREY1){
462 			gc = xgcsimplesrc0;
463 			if(xgcsimplecolor0 != sdval){
464 				XSetForeground(xdisplay, gc, sdval);
465 				xgcsimplecolor0 = sdval;
466 			}
467 			if(xgcsimplepm0 != mxm->pmid){
468 				XSetStipple(xdisplay, gc, mxm->pmid);
469 				xgcsimplepm0 = mxm->pmid;
470 			}
471 		}else{
472 		/* somehow this doesn't work on rob's mac
473 			gc = xgcsimplesrc;
474 			if(dst->chan == CMAP8 && xtblbit)
475 				sdval = plan9tox11[sdval];
476 
477 			if(xgcsimplecolor != sdval){
478 				XSetForeground(xdisplay, gc, sdval);
479 				xgcsimplecolor = sdval;
480 			}
481 			if(xgcsimplepm != mxm->pmid){
482 				XSetStipple(xdisplay, gc, mxm->pmid);
483 				xgcsimplepm = mxm->pmid;
484 			}
485 		*/
486 			return 0;
487 		}
488 		XSetTSOrigin(xdisplay, gc, p.x, p.y);
489 		XFillRectangle(xdisplay, dxm->pmid, gc, r.min.x, r.min.y, dx, dy);
490 		dirtyXdata(dst, par->r);
491 		return 1;
492 	}
493 	return 0;
494 }
495 
496 /*
497  * X11 window management and kernel hooks.
498  * Oh, how I loathe this code!
499  */
500 
501 static XColor			map[256];	/* Plan 9 colormap array */
502 static XColor			map7[128];	/* Plan 9 colormap array */
503 static uchar			map7to8[128][2];
504 static Colormap		xcmap;		/* Default shared colormap  */
505 
506 extern int mousequeue;
507 
508 /* for copy/paste, lifted from plan9ports */
509 static Atom clipboard;
510 static Atom utf8string;
511 static Atom targets;
512 static Atom text;
513 static Atom compoundtext;
514 
515 static	Drawable	xdrawable;
516 static	void		xexpose(XEvent*);
517 static	void		xmouse(XEvent*);
518 static	void		xkeyboard(XEvent*);
519 static	void		xmapping(XEvent*);
520 static	void		xdestroy(XEvent*);
521 static	void		xselect(XEvent*, XDisplay*);
522 static	void		xproc(void*);
523 static	Memimage*		xinitscreen(void);
524 static	void		initmap(Window);
525 static	GC		creategc(Drawable);
526 static	void		graphicscmap(XColor*);
527 static	int		xscreendepth;
528 static	XDisplay*	xkmcon;	/* used only in xproc */
529 static	XDisplay*	xsnarfcon;	/* used holding clip.lk */
530 static	ulong		xblack;
531 static	ulong		xwhite;
532 
533 static	int	putsnarf, assertsnarf;
534 
535 	Memimage *gscreen;
536 	Screeninfo screen;
537 
538 void
flushmemscreen(Rectangle r)539 flushmemscreen(Rectangle r)
540 {
541 	assert(!drawcanqlock());
542 	if(r.min.x >= r.max.x || r.min.y >= r.max.y)
543 		return;
544 	XCopyArea(xdisplay, xscreenid, xdrawable, xgccopy, r.min.x, r.min.y, Dx(r), Dy(r), r.min.x, r.min.y);
545 	XFlush(xdisplay);
546 }
547 
548 void
screeninit(void)549 screeninit(void)
550 {
551 	_memmkcmap();
552 
553 	gscreen = xinitscreen();
554 	kproc("xscreen", xproc, nil);
555 
556 	memimageinit();
557 	terminit();
558 	drawqlock();
559 	flushmemscreen(gscreen->r);
560 	drawqunlock();
561 }
562 
563 uchar*
attachscreen(Rectangle * r,ulong * chan,int * depth,int * width,int * softscreen,void ** X)564 attachscreen(Rectangle *r, ulong *chan, int *depth,
565 	int *width, int *softscreen, void **X)
566 {
567 	*r = gscreen->r;
568 	*chan = gscreen->chan;
569 	*depth = gscreen->depth;
570 	*width = gscreen->width;
571 	*X = gscreen->X;
572 	*softscreen = 1;
573 
574 	return gscreen->data->bdata;
575 }
576 
577 static int
revbyte(int b)578 revbyte(int b)
579 {
580 	int r;
581 
582 	r = 0;
583 	r |= (b&0x01) << 7;
584 	r |= (b&0x02) << 5;
585 	r |= (b&0x04) << 3;
586 	r |= (b&0x08) << 1;
587 	r |= (b&0x10) >> 1;
588 	r |= (b&0x20) >> 3;
589 	r |= (b&0x40) >> 5;
590 	r |= (b&0x80) >> 7;
591 	return r;
592 }
593 
594 void
mouseset(Point xy)595 mouseset(Point xy)
596 {
597 	drawqlock();
598 	XWarpPointer(xdisplay, None, xdrawable, 0, 0, 0, 0, xy.x, xy.y);
599 	XFlush(xdisplay);
600 	drawqunlock();
601 }
602 
603 static XCursor xcursor;
604 
605 void
setcursor(void)606 setcursor(void)
607 {
608 	XCursor xc;
609 	XColor fg, bg;
610 	Pixmap xsrc, xmask;
611 	int i;
612 	uchar src[2*16], mask[2*16];
613 
614 	for(i=0; i<2*16; i++){
615 		src[i] = revbyte(cursor.set[i]);
616 		mask[i] = revbyte(cursor.set[i] | cursor.clr[i]);
617 	}
618 
619 	drawqlock();
620 	fg = map[0];
621 	bg = map[255];
622 	xsrc = XCreateBitmapFromData(xdisplay, xdrawable, (char*)src, 16, 16);
623 	xmask = XCreateBitmapFromData(xdisplay, xdrawable, (char*)mask, 16, 16);
624 	xc = XCreatePixmapCursor(xdisplay, xsrc, xmask, &fg, &bg, -cursor.offset.x, -cursor.offset.y);
625 	if(xc != 0) {
626 		XDefineCursor(xdisplay, xdrawable, xc);
627 		if(xcursor != 0)
628 			XFreeCursor(xdisplay, xcursor);
629 		xcursor = xc;
630 	}
631 	XFreePixmap(xdisplay, xsrc);
632 	XFreePixmap(xdisplay, xmask);
633 	XFlush(xdisplay);
634 	drawqunlock();
635 }
636 
637 void
cursorarrow(void)638 cursorarrow(void)
639 {
640 	drawqlock();
641 	if(xcursor != 0){
642 		XFreeCursor(xdisplay, xcursor);
643 		xcursor = 0;
644 	}
645 	XUndefineCursor(xdisplay, xdrawable);
646 	XFlush(xdisplay);
647 	drawqunlock();
648 }
649 
650 static void
xproc(void * arg)651 xproc(void *arg)
652 {
653 	ulong mask;
654 	XEvent event;
655 
656 	mask = 	KeyPressMask|
657 		ButtonPressMask|
658 		ButtonReleaseMask|
659 		PointerMotionMask|
660 		Button1MotionMask|
661 		Button2MotionMask|
662 		Button3MotionMask|
663 		Button4MotionMask|
664 		Button5MotionMask|
665 		ExposureMask|
666 		StructureNotifyMask;
667 
668 	XSelectInput(xkmcon, xdrawable, mask);
669 	for(;;) {
670 		//XWindowEvent(xkmcon, xdrawable, mask, &event);
671 		XNextEvent(xkmcon, &event);
672 		xselect(&event, xkmcon);
673 		xkeyboard(&event);
674 		xmouse(&event);
675 		xexpose(&event);
676 		xmapping(&event);
677 		xdestroy(&event);
678 	}
679 }
680 
681 static int
shutup(XDisplay * d,XErrorEvent * e)682 shutup(XDisplay *d, XErrorEvent *e)
683 {
684 	char buf[200];
685 	iprint("X error: error code=%d, request_code=%d, minor=%d\n", e->error_code, e->request_code, e->minor_code);
686 	XGetErrorText(d, e->error_code, buf, sizeof(buf));
687 	iprint("%s\n", buf);
688 	USED(d);
689 	USED(e);
690 	return 0;
691 }
692 
693 static int
panicshutup(XDisplay * d)694 panicshutup(XDisplay *d)
695 {
696 	panic("x error");
697 	return -1;
698 }
699 
700 static Memimage*
xinitscreen(void)701 xinitscreen(void)
702 {
703 	Memimage *gscreen;
704 	int i, xsize, ysize, pmid;
705 	char *argv[2];
706 	char *disp_val;
707 	Window rootwin;
708 	Rectangle r;
709 	XWMHints hints;
710 	XScreen *screen;
711 	XVisualInfo xvi;
712 	int rootscreennum;
713 	XTextProperty name;
714 	XClassHint classhints;
715 	XSizeHints normalhints;
716 	XSetWindowAttributes attrs;
717 	XPixmapFormatValues *pfmt;
718 	int n;
719 	Pixmap icon_pixmap;
720 
721 	xscreenid = 0;
722 	xdrawable = 0;
723 
724 	xdisplay = XOpenDisplay(NULL);
725 	if(xdisplay == 0){
726 		iprint("xinitscreen: XOpenDisplay: %r [DISPLAY=%s]\n",
727 			getenv("DISPLAY"));
728 		exit(0);
729 	}
730 
731 	XSetErrorHandler(shutup);
732 	XSetIOErrorHandler(panicshutup);
733 	rootscreennum = DefaultScreen(xdisplay);
734 	rootwin = DefaultRootWindow(xdisplay);
735 
736 	xscreendepth = DefaultDepth(xdisplay, rootscreennum);
737 	if(XMatchVisualInfo(xdisplay, rootscreennum, 16, TrueColor, &xvi)
738 	|| XMatchVisualInfo(xdisplay, rootscreennum, 16, DirectColor, &xvi)){
739 		xvis = xvi.visual;
740 		xscreendepth = 16;
741 		xtblbit = 1;
742 	}
743 	else if(XMatchVisualInfo(xdisplay, rootscreennum, 24, TrueColor, &xvi)
744 	|| XMatchVisualInfo(xdisplay, rootscreennum, 24, DirectColor, &xvi)){
745 		xvis = xvi.visual;
746 		xscreendepth = 24;
747 		xtblbit = 1;
748 	}
749 	else if(XMatchVisualInfo(xdisplay, rootscreennum, 8, PseudoColor, &xvi)
750 	|| XMatchVisualInfo(xdisplay, rootscreennum, 8, StaticColor, &xvi)){
751 		if(xscreendepth > 8)
752 			panic("drawterm: can't deal with colormapped depth %d screens\n", xscreendepth);
753 		xvis = xvi.visual;
754 		xscreendepth = 8;
755 	}
756 	else{
757 		if(xscreendepth != 8)
758 			panic("drawterm: can't deal with depth %d screens\n", xscreendepth);
759 		xvis = DefaultVisual(xdisplay, rootscreennum);
760 	}
761 
762 	/*
763 	 * xscreendepth is only the number of significant pixel bits,
764 	 * not the total.  We need to walk the display list to find
765 	 * how many actual bits are being used per pixel.
766 	 */
767 	xscreenchan = 0; /* not a valid channel */
768 	pfmt = XListPixmapFormats(xdisplay, &n);
769 	for(i=0; i<n; i++){
770 		if(pfmt[i].depth == xscreendepth){
771 			switch(pfmt[i].bits_per_pixel){
772 			case 1:	/* untested */
773 				xscreenchan = GREY1;
774 				break;
775 			case 2:	/* untested */
776 				xscreenchan = GREY2;
777 				break;
778 			case 4:	/* untested */
779 				xscreenchan = GREY4;
780 				break;
781 			case 8:
782 				xscreenchan = CMAP8;
783 				break;
784 			case 16: /* uses 16 rather than 15, empirically. */
785 				xscreenchan = RGB16;
786 				break;
787 			case 24: /* untested (impossible?) */
788 				xscreenchan = RGB24;
789 				break;
790 			case 32:
791 				xscreenchan = CHAN4(CIgnore, 8, CRed, 8, CGreen, 8, CBlue, 8);
792 				break;
793 			}
794 		}
795 	}
796 	if(xscreenchan == 0)
797 		panic("drawterm: unknown screen pixel format\n");
798 
799 	screen = DefaultScreenOfDisplay(xdisplay);
800 	xcmap = DefaultColormapOfScreen(screen);
801 
802 	if(xvis->class != StaticColor){
803 		graphicscmap(map);
804 		initmap(rootwin);
805 	}
806 
807 	r.min = ZP;
808 	r.max.x = WidthOfScreen(screen);
809 	r.max.y = HeightOfScreen(screen);
810 
811 	xsize = Dx(r)*3/4;
812 	ysize = Dy(r)*3/4;
813 
814 	attrs.colormap = xcmap;
815 	attrs.background_pixel = 0;
816 	attrs.border_pixel = 0;
817 	/* attrs.override_redirect = 1;*/ /* WM leave me alone! |CWOverrideRedirect */
818 	xdrawable = XCreateWindow(xdisplay, rootwin, 0, 0, xsize, ysize, 0,
819 		xscreendepth, InputOutput, xvis, CWBackPixel|CWBorderPixel|CWColormap, &attrs);
820 
821 	/* load the given bitmap data and create an X pixmap containing it. */
822 	icon_pixmap = XCreateBitmapFromData(xdisplay,
823 		rootwin, (char *)glenda_bits,
824 		glenda_width, glenda_height);
825 
826 	/*
827 	 * set up property as required by ICCCM
828 	 */
829 	name.value = (uchar*)"drawterm";
830 	name.encoding = XA_STRING;
831 	name.format = 8;
832 	name.nitems = strlen((char*)name.value);
833 	normalhints.flags = USSize|PMaxSize;
834 	normalhints.max_width = Dx(r);
835 	normalhints.max_height = Dy(r);
836 	normalhints.width = xsize;
837 	normalhints.height = ysize;
838 	hints.flags = IconPixmapHint |InputHint|StateHint;
839 	hints.input = 1;
840 	hints.initial_state = NormalState;
841 	hints.icon_pixmap = icon_pixmap;
842 
843 	classhints.res_name = "drawterm";
844 	classhints.res_class = "Drawterm";
845 	argv[0] = "drawterm";
846 	argv[1] = nil;
847 	XSetWMProperties(xdisplay, xdrawable,
848 		&name,			/* XA_WM_NAME property for ICCCM */
849 		&name,			/* XA_WM_ICON_NAME */
850 		argv,			/* XA_WM_COMMAND */
851 		1,			/* argc */
852 		&normalhints,		/* XA_WM_NORMAL_HINTS */
853 		&hints,			/* XA_WM_HINTS */
854 		&classhints);		/* XA_WM_CLASS */
855 	XFlush(xdisplay);
856 
857 	/*
858 	 * put the window on the screen
859 	 */
860 	XMapWindow(xdisplay, xdrawable);
861 	XFlush(xdisplay);
862 
863 	xscreenid = XCreatePixmap(xdisplay, xdrawable, Dx(r), Dy(r), xscreendepth);
864 	gscreen = xallocmemimage(r, xscreenchan, xscreenid);
865 
866 	xgcfill = creategc(xscreenid);
867 	XSetFillStyle(xdisplay, xgcfill, FillSolid);
868 	xgccopy = creategc(xscreenid);
869 	xgcsimplesrc = creategc(xscreenid);
870 	XSetFillStyle(xdisplay, xgcsimplesrc, FillStippled);
871 	xgczero = creategc(xscreenid);
872 	xgcreplsrc = creategc(xscreenid);
873 	XSetFillStyle(xdisplay, xgcreplsrc, FillTiled);
874 
875 	pmid = XCreatePixmap(xdisplay, xdrawable, 1, 1, 1);
876 	xgcfill0 = creategc(pmid);
877 	XSetForeground(xdisplay, xgcfill0, 0);
878 	XSetFillStyle(xdisplay, xgcfill0, FillSolid);
879 	xgccopy0 = creategc(pmid);
880 	xgcsimplesrc0 = creategc(pmid);
881 	XSetFillStyle(xdisplay, xgcsimplesrc0, FillStippled);
882 	xgczero0 = creategc(pmid);
883 	xgcreplsrc0 = creategc(pmid);
884 	XSetFillStyle(xdisplay, xgcreplsrc0, FillTiled);
885 	XFreePixmap(xdisplay, pmid);
886 
887 	XSetForeground(xdisplay, xgccopy, plan9tox11[0]);
888 	XFillRectangle(xdisplay, xscreenid, xgccopy, 0, 0, xsize, ysize);
889 
890 	xkmcon = XOpenDisplay(NULL);
891 	if(xkmcon == 0){
892 		disp_val = getenv("DISPLAY");
893 		if(disp_val == 0)
894 			disp_val = "not set";
895 		iprint("drawterm: open %r, DISPLAY is %s\n", disp_val);
896 		exit(0);
897 	}
898 	xsnarfcon = XOpenDisplay(NULL);
899 	if(xsnarfcon == 0){
900 		disp_val = getenv("DISPLAY");
901 		if(disp_val == 0)
902 			disp_val = "not set";
903 		iprint("drawterm: open %r, DISPLAY is %s\n", disp_val);
904 		exit(0);
905 	}
906 
907 	clipboard = XInternAtom(xkmcon, "CLIPBOARD", False);
908 	utf8string = XInternAtom(xkmcon, "UTF8_STRING", False);
909 	targets = XInternAtom(xkmcon, "TARGETS", False);
910 	text = XInternAtom(xkmcon, "TEXT", False);
911 	compoundtext = XInternAtom(xkmcon, "COMPOUND_TEXT", False);
912 
913 	xblack = screen->black_pixel;
914 	xwhite = screen->white_pixel;
915 	return gscreen;
916 }
917 
918 static void
graphicscmap(XColor * map)919 graphicscmap(XColor *map)
920 {
921 	int r, g, b, cr, cg, cb, v, num, den, idx, v7, idx7;
922 
923 	for(r=0; r!=4; r++) {
924 		for(g = 0; g != 4; g++) {
925 			for(b = 0; b!=4; b++) {
926 				for(v = 0; v!=4; v++) {
927 					den=r;
928 					if(g > den)
929 						den=g;
930 					if(b > den)
931 						den=b;
932 					/* divide check -- pick grey shades */
933 					if(den==0)
934 						cr=cg=cb=v*17;
935 					else {
936 						num=17*(4*den+v);
937 						cr=r*num/den;
938 						cg=g*num/den;
939 						cb=b*num/den;
940 					}
941 					idx = r*64 + v*16 + ((g*4 + b + v - r) & 15);
942 					map[idx].red = cr*0x0101;
943 					map[idx].green = cg*0x0101;
944 					map[idx].blue = cb*0x0101;
945 					map[idx].pixel = idx;
946 					map[idx].flags = DoRed|DoGreen|DoBlue;
947 
948 					v7 = v >> 1;
949 					idx7 = r*32 + v7*16 + g*4 + b;
950 					if((v & 1) == v7){
951 						map7to8[idx7][0] = idx;
952 						if(den == 0) { 		/* divide check -- pick grey shades */
953 							cr = ((255.0/7.0)*v7)+0.5;
954 							cg = cr;
955 							cb = cr;
956 						}
957 						else {
958 							num=17*15*(4*den+v7*2)/14;
959 							cr=r*num/den;
960 							cg=g*num/den;
961 							cb=b*num/den;
962 						}
963 						map7[idx7].red = cr*0x0101;
964 						map7[idx7].green = cg*0x0101;
965 						map7[idx7].blue = cb*0x0101;
966 						map7[idx7].pixel = idx7;
967 						map7[idx7].flags = DoRed|DoGreen|DoBlue;
968 					}
969 					else
970 						map7to8[idx7][1] = idx;
971 				}
972 			}
973 		}
974 	}
975 }
976 
977 /*
978  * Initialize and install the drawterm colormap as a private colormap for this
979  * application.  Drawterm gets the best colors here when it has the cursor focus.
980  */
981 static void
initmap(Window w)982 initmap(Window w)
983 {
984 	XColor c;
985 	int i;
986 	ulong p, pp;
987 	char buf[30];
988 
989 	if(xscreendepth <= 1)
990 		return;
991 
992 	if(xscreendepth >= 24) {
993 		/* The pixel value returned from XGetPixel needs to
994 		 * be converted to RGB so we can call rgb2cmap()
995 		 * to translate between 24 bit X and our color. Unfortunately,
996 		 * the return value appears to be display server endian
997 		 * dependant. Therefore, we run some heuristics to later
998 		 * determine how to mask the int value correctly.
999 		 * Yeah, I know we can look at xvis->byte_order but
1000 		 * some displays say MSB even though they run on LSB.
1001 		 * Besides, this is more anal.
1002 		 */
1003 		if(xscreendepth != DefaultDepth(xdisplay, DefaultScreen(xdisplay)))
1004 			xcmap = XCreateColormap(xdisplay, w, xvis, AllocNone);
1005 
1006 		c = map[19];
1007 		/* find out index into colormap for our RGB */
1008 		if(!XAllocColor(xdisplay, xcmap, &c))
1009 			panic("drawterm: screen-x11 can't alloc color");
1010 
1011 		p  = c.pixel;
1012 		pp = rgb2cmap((p>>16)&0xff,(p>>8)&0xff,p&0xff);
1013 		if(pp!=map[19].pixel) {
1014 			/* check if endian is other way */
1015 			pp = rgb2cmap(p&0xff,(p>>8)&0xff,(p>>16)&0xff);
1016 			if(pp!=map[19].pixel)
1017 				panic("cannot detect x server byte order");
1018 			switch(xscreenchan){
1019 			case RGB24:
1020 				xscreenchan = BGR24;
1021 				break;
1022 			case XRGB32:
1023 				xscreenchan = XBGR32;
1024 				break;
1025 			default:
1026 				panic("don't know how to byteswap channel %s",
1027 					chantostr(buf, xscreenchan));
1028 				break;
1029 			}
1030 		}
1031 	} else if(xvis->class == TrueColor || xvis->class == DirectColor) {
1032 	} else if(xvis->class == PseudoColor) {
1033 		if(xtblbit == 0){
1034 			xcmap = XCreateColormap(xdisplay, w, xvis, AllocAll);
1035 			XStoreColors(xdisplay, xcmap, map, 256);
1036 			for(i = 0; i < 256; i++) {
1037 				plan9tox11[i] = i;
1038 				x11toplan9[i] = i;
1039 			}
1040 		}
1041 		else {
1042 			for(i = 0; i < 128; i++) {
1043 				c = map7[i];
1044 				if(!XAllocColor(xdisplay, xcmap, &c)) {
1045 					iprint("drawterm: can't alloc colors in default map, don't use -7\n");
1046 					exit(0);
1047 				}
1048 				plan9tox11[map7to8[i][0]] = c.pixel;
1049 				plan9tox11[map7to8[i][1]] = c.pixel;
1050 				x11toplan9[c.pixel] = map7to8[i][0];
1051 			}
1052 		}
1053 	}
1054 	else
1055 		panic("drawterm: unsupported visual class %d\n", xvis->class);
1056 }
1057 
1058 static void
xdestroy(XEvent * e)1059 xdestroy(XEvent *e)
1060 {
1061 	XDestroyWindowEvent *xe;
1062 	if(e->type != DestroyNotify)
1063 		return;
1064 	xe = (XDestroyWindowEvent*)e;
1065 	if(xe->window == xdrawable)
1066 		exit(0);
1067 }
1068 
1069 static void
xmapping(XEvent * e)1070 xmapping(XEvent *e)
1071 {
1072 	XMappingEvent *xe;
1073 
1074 	if(e->type != MappingNotify)
1075 		return;
1076 	xe = (XMappingEvent*)e;
1077 	USED(xe);
1078 }
1079 
1080 
1081 /*
1082  * Disable generation of GraphicsExpose/NoExpose events in the GC.
1083  */
1084 static GC
creategc(Drawable d)1085 creategc(Drawable d)
1086 {
1087 	XGCValues gcv;
1088 
1089 	gcv.function = GXcopy;
1090 	gcv.graphics_exposures = False;
1091 	return XCreateGC(xdisplay, d, GCFunction|GCGraphicsExposures, &gcv);
1092 }
1093 
1094 static void
xexpose(XEvent * e)1095 xexpose(XEvent *e)
1096 {
1097 	Rectangle r;
1098 	XExposeEvent *xe;
1099 
1100 	if(e->type != Expose)
1101 		return;
1102 	xe = (XExposeEvent*)e;
1103 	r.min.x = xe->x;
1104 	r.min.y = xe->y;
1105 	r.max.x = xe->x + xe->width;
1106 	r.max.y = xe->y + xe->height;
1107 	drawflushr(r);
1108 }
1109 
1110 static void
xkeyboard(XEvent * e)1111 xkeyboard(XEvent *e)
1112 {
1113 	KeySym k;
1114 
1115 	/*
1116 	 * I tried using XtGetActionKeysym, but it didn't seem to
1117 	 * do case conversion properly
1118 	 * (at least, with Xterminal servers and R4 intrinsics)
1119 	 */
1120 	if(e->xany.type != KeyPress)
1121 		return;
1122 
1123 
1124 	XLookupString((XKeyEvent*)e, NULL, 0, &k, NULL);
1125 
1126 	if(k == XK_Multi_key || k == NoSymbol)
1127 		return;
1128 	if(k&0xFF00){
1129 		switch(k){
1130 		case XK_BackSpace:
1131 		case XK_Tab:
1132 		case XK_Escape:
1133 		case XK_Delete:
1134 		case XK_KP_0:
1135 		case XK_KP_1:
1136 		case XK_KP_2:
1137 		case XK_KP_3:
1138 		case XK_KP_4:
1139 		case XK_KP_5:
1140 		case XK_KP_6:
1141 		case XK_KP_7:
1142 		case XK_KP_8:
1143 		case XK_KP_9:
1144 		case XK_KP_Divide:
1145 		case XK_KP_Multiply:
1146 		case XK_KP_Subtract:
1147 		case XK_KP_Add:
1148 		case XK_KP_Decimal:
1149 			k &= 0x7F;
1150 			break;
1151 		case XK_Linefeed:
1152 			k = '\r';
1153 			break;
1154 		case XK_KP_Space:
1155 			k = ' ';
1156 			break;
1157 		case XK_Home:
1158 		case XK_KP_Home:
1159 			k = Khome;
1160 			break;
1161 		case XK_Left:
1162 		case XK_KP_Left:
1163 			k = Kleft;
1164 			break;
1165 		case XK_Up:
1166 		case XK_KP_Up:
1167 			k = Kup;
1168 			break;
1169 		case XK_Down:
1170 		case XK_KP_Down:
1171 			k = Kdown;
1172 			break;
1173 		case XK_Right:
1174 		case XK_KP_Right:
1175 			k = Kright;
1176 			break;
1177 		case XK_Page_Down:
1178 		case XK_KP_Page_Down:
1179 			k = Kpgdown;
1180 			break;
1181 		case XK_End:
1182 		case XK_KP_End:
1183 			k = Kend;
1184 			break;
1185 		case XK_Page_Up:
1186 		case XK_KP_Page_Up:
1187 			k = Kpgup;
1188 			break;
1189 		case XK_Insert:
1190 		case XK_KP_Insert:
1191 			k = Kins;
1192 			break;
1193 		case XK_KP_Enter:
1194 		case XK_Return:
1195 			k = '\n';
1196 			break;
1197 		case XK_Alt_L:
1198 		case XK_Alt_R:
1199 			k = Kalt;
1200 			break;
1201 		case XK_F1:
1202 		case XK_F2:
1203 		case XK_F3:
1204 		case XK_F4:
1205 		case XK_F5:
1206 		case XK_F6:
1207 		case XK_F7:
1208 		case XK_F8:
1209 		case XK_F9:
1210 		case XK_F10:
1211 		case XK_F11:
1212 		case XK_F12:
1213 			k = KF|(k - XK_F1 + 1);
1214 			break;
1215 		case XK_Shift_L:
1216 		case XK_Shift_R:
1217 		case XK_Control_L:
1218 		case XK_Control_R:
1219 		case XK_Caps_Lock:
1220 		case XK_Shift_Lock:
1221 
1222 		case XK_Meta_L:
1223 		case XK_Meta_R:
1224 		case XK_Super_L:
1225 		case XK_Super_R:
1226 		case XK_Hyper_L:
1227 		case XK_Hyper_R:
1228 			return;
1229 		default:		/* not ISO-1 or tty control */
1230   			if(k>0xff){
1231 				k = keysym2ucs(k); /* supplied by X */
1232 				if(k == -1)
1233 					return;
1234 			}
1235 			break;
1236 		}
1237 	}
1238 
1239 	/* Compensate for servers that call a minus a hyphen */
1240 	if(k == XK_hyphen)
1241 		k = XK_minus;
1242 	/* Do control mapping ourselves if translator doesn't */
1243 	if(e->xkey.state&ControlMask && k != Kalt)
1244 		k &= 0x9f;
1245 	if(k == NoSymbol) {
1246 		return;
1247 	}
1248 
1249 	kbdputc(kbdq, k);
1250 }
1251 
1252 static void
xmouse(XEvent * e)1253 xmouse(XEvent *e)
1254 {
1255 	Mousestate ms;
1256 	int i, s;
1257 	XButtonEvent *be;
1258 	XMotionEvent *me;
1259 
1260 	if(putsnarf != assertsnarf){
1261 		assertsnarf = putsnarf;
1262 		XSetSelectionOwner(xkmcon, XA_PRIMARY, xdrawable, CurrentTime);
1263 		if(clipboard != None)
1264 			XSetSelectionOwner(xkmcon, clipboard, xdrawable, CurrentTime);
1265 		XFlush(xkmcon);
1266 	}
1267 
1268 	switch(e->type){
1269 	case ButtonPress:
1270 		be = (XButtonEvent *)e;
1271 		/*
1272 		 * Fake message, just sent to make us announce snarf.
1273 		 * Apparently state and button are 16 and 8 bits on
1274 		 * the wire, since they are truncated by the time they
1275 		 * get to us.
1276 		 */
1277 		if(be->send_event
1278 		&& (~be->state&0xFFFF)==0
1279 		&& (~be->button&0xFF)==0)
1280 			return;
1281 		ms.xy.x = be->x;
1282 		ms.xy.y = be->y;
1283 		s = be->state;
1284 		ms.msec = be->time;
1285 		switch(be->button){
1286 		case 1:
1287 			s |= Button1Mask;
1288 			break;
1289 		case 2:
1290 			s |= Button2Mask;
1291 			break;
1292 		case 3:
1293 			s |= Button3Mask;
1294 			break;
1295 		case 4:
1296 			s |= Button4Mask;
1297 			break;
1298 		case 5:
1299 			s |= Button5Mask;
1300 			break;
1301 		}
1302 		break;
1303 	case ButtonRelease:
1304 		be = (XButtonEvent *)e;
1305 		ms.xy.x = be->x;
1306 		ms.xy.y = be->y;
1307 		ms.msec = be->time;
1308 		s = be->state;
1309 		switch(be->button){
1310 		case 1:
1311 			s &= ~Button1Mask;
1312 			break;
1313 		case 2:
1314 			s &= ~Button2Mask;
1315 			break;
1316 		case 3:
1317 			s &= ~Button3Mask;
1318 			break;
1319 		case 4:
1320 			s &= ~Button4Mask;
1321 			break;
1322 		case 5:
1323 			s &= ~Button5Mask;
1324 			break;
1325 		}
1326 		break;
1327 	case MotionNotify:
1328 		me = (XMotionEvent *)e;
1329 		s = me->state;
1330 		ms.xy.x = me->x;
1331 		ms.xy.y = me->y;
1332 		ms.msec = me->time;
1333 		break;
1334 	default:
1335 		return;
1336 	}
1337 
1338 	ms.buttons = 0;
1339 	if(s & Button1Mask)
1340 		ms.buttons |= 1;
1341 	if(s & Button2Mask)
1342 		ms.buttons |= 2;
1343 	if(s & Button3Mask)
1344 		ms.buttons |= 4;
1345 	if(s & Button4Mask)
1346 		ms.buttons |= 8;
1347 	if(s & Button5Mask)
1348 		ms.buttons |= 16;
1349 
1350 	lock(&mouse.lk);
1351 	i = mouse.wi;
1352 	if(mousequeue) {
1353 		if(i == mouse.ri || mouse.lastb != ms.buttons || mouse.trans) {
1354 			mouse.wi = (i+1)%Mousequeue;
1355 			if(mouse.wi == mouse.ri)
1356 				mouse.ri = (mouse.ri+1)%Mousequeue;
1357 			mouse.trans = mouse.lastb != ms.buttons;
1358 		} else {
1359 			i = (i-1+Mousequeue)%Mousequeue;
1360 		}
1361 	} else {
1362 		mouse.wi = (i+1)%Mousequeue;
1363 		mouse.ri = i;
1364 	}
1365 	mouse.queue[i] = ms;
1366 	mouse.lastb = ms.buttons;
1367 	unlock(&mouse.lk);
1368 	wakeup(&mouse.r);
1369 }
1370 
1371 void
getcolor(ulong i,ulong * r,ulong * g,ulong * b)1372 getcolor(ulong i, ulong *r, ulong *g, ulong *b)
1373 {
1374 	ulong v;
1375 
1376 	v = cmap2rgb(i);
1377 	*r = (v>>16)&0xFF;
1378 	*g = (v>>8)&0xFF;
1379 	*b = v&0xFF;
1380 }
1381 
1382 void
setcolor(ulong i,ulong r,ulong g,ulong b)1383 setcolor(ulong i, ulong r, ulong g, ulong b)
1384 {
1385 	/* no-op */
1386 }
1387 
1388 int
atlocalconsole(void)1389 atlocalconsole(void)
1390 {
1391 	char *p, *q;
1392 	char buf[128];
1393 
1394 	p = getenv("DRAWTERM_ATLOCALCONSOLE");
1395 	if(p && atoi(p) == 1)
1396 		return 1;
1397 
1398 	p = getenv("DISPLAY");
1399 	if(p == nil)
1400 		return 0;
1401 
1402 	/* extract host part */
1403 	q = strchr(p, ':');
1404 	if(q == nil)
1405 		return 0;
1406 	*q = 0;
1407 
1408 	if(strcmp(p, "") == 0)
1409 		return 1;
1410 
1411 	/* try to match against system name (i.e. for ssh) */
1412 	if(gethostname(buf, sizeof buf) == 0){
1413 		if(strcmp(p, buf) == 0)
1414 			return 1;
1415 		if(strncmp(p, buf, strlen(p)) == 0 && buf[strlen(p)]=='.')
1416 			return 1;
1417 	}
1418 
1419 	return 0;
1420 }
1421 
1422 /*
1423  * Cut and paste.  Just couldn't stand to make this simple...
1424  */
1425 
1426 typedef struct Clip Clip;
1427 struct Clip
1428 {
1429 	char buf[SnarfSize];
1430 	QLock lk;
1431 };
1432 Clip clip;
1433 
1434 #undef long	/* sic */
1435 #undef ulong
1436 
1437 static char*
_xgetsnarf(XDisplay * xd)1438 _xgetsnarf(XDisplay *xd)
1439 {
1440 	uchar *data, *xdata;
1441 	Atom clipboard, type, prop;
1442 	unsigned long lastlen;
1443 	unsigned long dummy, len;
1444 	int fmt, i;
1445 	Window w;
1446 
1447 	qlock(&clip.lk);
1448 	/*
1449 	 * Have we snarfed recently and the X server hasn't caught up?
1450 	 */
1451 	if(putsnarf != assertsnarf)
1452 		goto mine;
1453 
1454 	/*
1455 	 * Is there a primary selection (highlighted text in an xterm)?
1456 	 */
1457 	clipboard = XA_PRIMARY;
1458 	w = XGetSelectionOwner(xd, XA_PRIMARY);
1459 	if(w == xdrawable){
1460 	mine:
1461 		data = (uchar*)strdup(clip.buf);
1462 		goto out;
1463 	}
1464 
1465 	/*
1466 	 * If not, is there a clipboard selection?
1467 	 */
1468 	if(w == None && clipboard != None){
1469 		clipboard = clipboard;
1470 		w = XGetSelectionOwner(xd, clipboard);
1471 		if(w == xdrawable)
1472 			goto mine;
1473 	}
1474 
1475 	/*
1476 	 * If not, give up.
1477 	 */
1478 	if(w == None){
1479 		data = nil;
1480 		goto out;
1481 	}
1482 
1483 	/*
1484 	 * We should be waiting for SelectionNotify here, but it might never
1485 	 * come, and we have no way to time out.  Instead, we will clear
1486 	 * local property #1, request our buddy to fill it in for us, and poll
1487 	 * until he's done or we get tired of waiting.
1488 	 *
1489 	 * We should try to go for utf8string instead of XA_STRING,
1490 	 * but that would add to the polling.
1491 	 */
1492 	prop = 1;
1493 	XChangeProperty(xd, xdrawable, prop, XA_STRING, 8, PropModeReplace, (uchar*)"", 0);
1494 	XConvertSelection(xd, clipboard, XA_STRING, prop, xdrawable, CurrentTime);
1495 	XFlush(xd);
1496 	lastlen = 0;
1497 	for(i=0; i<10 || (lastlen!=0 && i<30); i++){
1498 		usleep(100*1000);
1499 		XGetWindowProperty(xd, xdrawable, prop, 0, 0, 0, AnyPropertyType,
1500 			&type, &fmt, &dummy, &len, &data);
1501 		if(lastlen == len && len > 0)
1502 			break;
1503 		lastlen = len;
1504 	}
1505 	if(i == 10){
1506 		data = nil;
1507 		goto out;
1508 	}
1509 	/* get the property */
1510 	data = nil;
1511 	XGetWindowProperty(xd, xdrawable, prop, 0, SnarfSize/sizeof(unsigned long), 0,
1512 		AnyPropertyType, &type, &fmt, &len, &dummy, &xdata);
1513 	if((type != XA_STRING && type != utf8string) || len == 0){
1514 		if(xdata)
1515 			XFree(xdata);
1516 		data = nil;
1517 	}else{
1518 		if(xdata){
1519 			data = (uchar*)strdup((char*)xdata);
1520 			XFree(xdata);
1521 		}else
1522 			data = nil;
1523 	}
1524 out:
1525 	qunlock(&clip.lk);
1526 	return (char*)data;
1527 }
1528 
1529 static void
_xputsnarf(XDisplay * xd,char * data)1530 _xputsnarf(XDisplay *xd, char *data)
1531 {
1532 	XButtonEvent e;
1533 
1534 	if(strlen(data) >= SnarfSize)
1535 		return;
1536 	qlock(&clip.lk);
1537 	strcpy(clip.buf, data);
1538 
1539 	/* leave note for mouse proc to assert selection ownership */
1540 	putsnarf++;
1541 
1542 	/* send mouse a fake event so snarf is announced */
1543 	memset(&e, 0, sizeof e);
1544 	e.type = ButtonPress;
1545 	e.window = xdrawable;
1546 	e.state = ~0;
1547 	e.button = ~0;
1548 	XSendEvent(xd, xdrawable, True, ButtonPressMask, (XEvent*)&e);
1549 	XFlush(xd);
1550 	qunlock(&clip.lk);
1551 }
1552 
1553 static void
xselect(XEvent * e,XDisplay * xd)1554 xselect(XEvent *e, XDisplay *xd)
1555 {
1556 	char *name;
1557 	XEvent r;
1558 	XSelectionRequestEvent *xe;
1559 	Atom a[4];
1560 
1561 	if(e->xany.type != SelectionRequest)
1562 		return;
1563 
1564 	memset(&r, 0, sizeof r);
1565 	xe = (XSelectionRequestEvent*)e;
1566 if(0) iprint("xselect target=%d requestor=%d property=%d selection=%d\n",
1567 	xe->target, xe->requestor, xe->property, xe->selection);
1568 	r.xselection.property = xe->property;
1569 	if(xe->target == targets){
1570 		a[0] = XA_STRING;
1571 		a[1] = utf8string;
1572 		a[2] = text;
1573 		a[3] = compoundtext;
1574 
1575 		XChangeProperty(xd, xe->requestor, xe->property, XA_ATOM,
1576 			32, PropModeReplace, (uchar*)a, sizeof a);
1577 	}else if(xe->target == XA_STRING || xe->target == utf8string || xe->target == text || xe->target == compoundtext){
1578 	text:
1579 		/* if the target is STRING we're supposed to reply with Latin1 XXX */
1580 		qlock(&clip.lk);
1581 		XChangeProperty(xd, xe->requestor, xe->property, xe->target,
1582 			8, PropModeReplace, (uchar*)clip.buf, strlen(clip.buf));
1583 		qunlock(&clip.lk);
1584 	}else{
1585 		name = XGetAtomName(xd, xe->target);
1586 		if(name == nil)
1587 			iprint("XGetAtomName %d failed\n", xe->target);
1588 		if(name){
1589 			if(strcmp(name, "TIMESTAMP") == 0){
1590 				/* nothing */
1591 			}else if(strncmp(name, "image/", 6) == 0){
1592 				/* nothing */
1593 			}else if(strcmp(name, "text/html") == 0){
1594 				/* nothing */
1595 			}else if(strcmp(name, "text/plain") == 0 || strcmp(name, "text/plain;charset=UTF-8") == 0){
1596 				goto text;
1597 			}else
1598 				iprint("%s: cannot handle selection request for '%s' (%d)\n", argv0, name, (int)xe->target);
1599 		}
1600 		r.xselection.property = None;
1601 	}
1602 
1603 	r.xselection.display = xe->display;
1604 	/* r.xselection.property filled above */
1605 	r.xselection.target = xe->target;
1606 	r.xselection.type = SelectionNotify;
1607 	r.xselection.requestor = xe->requestor;
1608 	r.xselection.time = xe->time;
1609 	r.xselection.send_event = True;
1610 	r.xselection.selection = xe->selection;
1611 	XSendEvent(xd, xe->requestor, False, 0, &r);
1612 	XFlush(xd);
1613 }
1614 
1615 char*
clipread(void)1616 clipread(void)
1617 {
1618 	return _xgetsnarf(xsnarfcon);
1619 }
1620 
1621 int
clipwrite(char * buf)1622 clipwrite(char *buf)
1623 {
1624 	_xputsnarf(xsnarfcon, buf);
1625 	return 0;
1626 }
1627 
1628