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