tx11-screen.c - plan9port - [fork] Plan 9 from user space
HTML git clone git://src.adamsgaard.dk/plan9port
DIR Log
DIR Files
DIR Refs
DIR README
DIR LICENSE
---
tx11-screen.c (39053B)
---
1 #include <u.h>
2 #include "x11-inc.h"
3 #include "x11-keysym2ucs.h"
4 #include <errno.h>
5 #include <libc.h>
6 #include <draw.h>
7 #include <memdraw.h>
8 #include <memlayer.h>
9 #include <keyboard.h>
10 #include <mouse.h>
11 #include <cursor.h>
12 #include <thread.h>
13 #include "x11-memdraw.h"
14 #include "devdraw.h"
15
16 #undef time
17
18 static void plan9cmap(void);
19 static int setupcmap(XWindow);
20 static XGC xgc(XDrawable, int, int);
21 #define Mask MouseMask|ExposureMask|StructureNotifyMask|KeyPressMask|KeyReleaseMask|EnterWindowMask|LeaveWindowMask|FocusChangeMask
22
23 #define MouseMask (\
24 ButtonPressMask|\
25 ButtonReleaseMask|\
26 PointerMotionMask|\
27 Button1MotionMask|\
28 Button2MotionMask|\
29 Button3MotionMask)
30
31 Xprivate _x;
32
33 static void runxevent(XEvent *xev);
34 static int _xconfigure(Xwin *w, XEvent *e);
35 static int _xdestroy(Xwin *w, XEvent *e);
36 static void _xexpose(Xwin *w, XEvent *e);
37 static int _xreplacescreenimage(Client *client);
38 static int _xtoplan9mouse(Xwin *w, XEvent *e, Mouse *m);
39 static void _xmovewindow(Xwin *w, Rectangle r);
40 static int _xtoplan9kbd(XEvent *e);
41 static int _xselect(XEvent *e);
42
43 static void rpc_resizeimg(Client*);
44 static void rpc_resizewindow(Client*, Rectangle);
45 static void rpc_setcursor(Client*, Cursor*, Cursor2*);
46 static void rpc_setlabel(Client*, char*);
47 static void rpc_setmouse(Client*, Point);
48 static void rpc_topwin(Client*);
49 static void rpc_bouncemouse(Client*, Mouse);
50 static void rpc_flush(Client*, Rectangle);
51
52 static ClientImpl x11impl = {
53 rpc_resizeimg,
54 rpc_resizewindow,
55 rpc_setcursor,
56 rpc_setlabel,
57 rpc_setmouse,
58 rpc_topwin,
59 rpc_bouncemouse,
60 rpc_flush
61 };
62
63 static Xwin*
64 newxwin(Client *c)
65 {
66 Xwin *w;
67
68 w = mallocz(sizeof *w, 1);
69 if(w == nil)
70 sysfatal("out of memory");
71 w->client = c;
72 w->next = _x.windows;
73 _x.windows = w;
74 c->impl = &x11impl;
75 c->view = w;
76 return w;
77 }
78
79 static Xwin*
80 findxwin(XDrawable d)
81 {
82 Xwin *w, **l;
83
84 for(l=&_x.windows; (w=*l) != nil; l=&w->next) {
85 if(w->drawable == d) {
86 /* move to front */
87 *l = w->next;
88 w->next = _x.windows;
89 _x.windows = w;
90 return w;
91 }
92 }
93 return nil;
94 }
95
96 static int
97 xerror(XDisplay *d, XErrorEvent *e)
98 {
99 char buf[200];
100
101 if(e->request_code == 42) /* XSetInputFocus */
102 return 0;
103 if(e->request_code == 18) /* XChangeProperty */
104 return 0;
105 /*
106 * BadDrawable happens in apps that get resized a LOT,
107 * e.g. when KDE is configured to resize continuously
108 * during a window drag.
109 */
110 if(e->error_code == 9) /* BadDrawable */
111 return 0;
112
113 fprint(2, "X error: error_code=%d, request_code=%d, minor=%d disp=%p\n",
114 e->error_code, e->request_code, e->minor_code, d);
115 XGetErrorText(d, e->error_code, buf, sizeof buf);
116 fprint(2, "%s\n", buf);
117 return 0;
118 }
119
120 static int
121 xioerror(XDisplay *d)
122 {
123 /*print("X I/O error\n"); */
124 exit(0);
125 /*sysfatal("X I/O error\n");*/
126 abort();
127 return -1;
128 }
129
130 static void xloop(void);
131
132 static QLock xlk;
133
134 void
135 xlock(void)
136 {
137 qlock(&xlk);
138 }
139
140 void
141 xunlock(void)
142 {
143 qunlock(&xlk);
144 }
145
146 void
147 gfx_main(void)
148 {
149 char *disp;
150 int i, n, xrootid;
151 XPixmapFormatValues *pfmt;
152 XScreen *xscreen;
153 XVisualInfo xvi;
154 XWindow xrootwin;
155
156 /*
157 if(XInitThreads() == 0)
158 sysfatal("XInitThread: %r");
159 */
160
161 /*
162 * Connect to X server.
163 */
164 _x.display = XOpenDisplay(NULL);
165 if(_x.display == nil){
166 disp = getenv("DISPLAY");
167 werrstr("XOpenDisplay %s: %r", disp ? disp : ":0");
168 free(disp);
169 sysfatal("%r");
170 }
171 _x.fd = ConnectionNumber(_x.display);
172 XSetErrorHandler(xerror);
173 XSetIOErrorHandler(xioerror);
174 xrootid = DefaultScreen(_x.display);
175 xrootwin = DefaultRootWindow(_x.display);
176
177 /*
178 * Figure out underlying screen format.
179 */
180 if(XMatchVisualInfo(_x.display, xrootid, 24, TrueColor, &xvi)
181 || XMatchVisualInfo(_x.display, xrootid, 24, DirectColor, &xvi)){
182 _x.vis = xvi.visual;
183 _x.depth = 24;
184 }
185 else
186 if(XMatchVisualInfo(_x.display, xrootid, 16, TrueColor, &xvi)
187 || XMatchVisualInfo(_x.display, xrootid, 16, DirectColor, &xvi)){
188 _x.vis = xvi.visual;
189 _x.depth = 16;
190 }
191 else
192 if(XMatchVisualInfo(_x.display, xrootid, 15, TrueColor, &xvi)
193 || XMatchVisualInfo(_x.display, xrootid, 15, DirectColor, &xvi)){
194 _x.vis = xvi.visual;
195 _x.depth = 15;
196 }
197 else
198 if(XMatchVisualInfo(_x.display, xrootid, 8, PseudoColor, &xvi)
199 || XMatchVisualInfo(_x.display, xrootid, 8, StaticColor, &xvi)){
200 if(_x.depth > 8){
201 werrstr("can't deal with colormapped depth %d screens",
202 _x.depth);
203 goto err0;
204 }
205 _x.vis = xvi.visual;
206 _x.depth = 8;
207 }
208 else{
209 _x.depth = DefaultDepth(_x.display, xrootid);
210 if(_x.depth != 8){
211 werrstr("can't understand depth %d screen", _x.depth);
212 goto err0;
213 }
214 _x.vis = DefaultVisual(_x.display, xrootid);
215 }
216
217 if(DefaultDepth(_x.display, xrootid) == _x.depth)
218 _x.usetable = 1;
219
220 /*
221 * _x.depth is only the number of significant pixel bits,
222 * not the total number of pixel bits. We need to walk the
223 * display list to find how many actual bits are used
224 * per pixel.
225 */
226 _x.chan = 0;
227 pfmt = XListPixmapFormats(_x.display, &n);
228 for(i=0; i<n; i++){
229 if(pfmt[i].depth == _x.depth){
230 switch(pfmt[i].bits_per_pixel){
231 case 1: /* untested */
232 _x.chan = GREY1;
233 break;
234 case 2: /* untested */
235 _x.chan = GREY2;
236 break;
237 case 4: /* untested */
238 _x.chan = GREY4;
239 break;
240 case 8:
241 _x.chan = CMAP8;
242 break;
243 case 15:
244 _x.chan = RGB15;
245 break;
246 case 16: /* how to tell RGB15? */
247 _x.chan = RGB16;
248 break;
249 case 24: /* untested (impossible?) */
250 _x.chan = RGB24;
251 break;
252 case 32:
253 _x.chan = XRGB32;
254 break;
255 }
256 }
257 }
258 XFree(pfmt);
259 if(_x.chan == 0){
260 werrstr("could not determine screen pixel format");
261 goto err0;
262 }
263
264 /*
265 * Set up color map if necessary.
266 */
267 xscreen = DefaultScreenOfDisplay(_x.display);
268 _x.cmap = DefaultColormapOfScreen(xscreen);
269 if(_x.vis->class != StaticColor){
270 plan9cmap();
271 setupcmap(xrootwin);
272 }
273 gfx_started();
274 xloop();
275
276 err0:
277 XCloseDisplay(_x.display);
278 sysfatal("%r");
279 }
280
281 static void
282 xloop(void)
283 {
284 fd_set rd, wr, xx;
285 XEvent event;
286
287 xlock();
288 _x.fd = ConnectionNumber(_x.display);
289 for(;;) {
290 FD_ZERO(&rd);
291 FD_ZERO(&wr);
292 FD_ZERO(&xx);
293 FD_SET(_x.fd, &rd);
294 FD_SET(_x.fd, &xx);
295 if(_x.windows != nil)
296 XSelectInput(_x.display, _x.windows->drawable, Mask); // TODO: when is this needed?
297 XFlush(_x.display);
298 xunlock();
299
300 again:
301 if(select(_x.fd+1, &rd, &wr, &xx, nil) < 0) {
302 if(errno == EINTR)
303 goto again;
304 sysfatal("select: %r"); // TODO: quiet exit?
305 }
306
307 xlock();
308 while(XPending(_x.display)) {
309 XNextEvent(_x.display, &event);
310 runxevent(&event);
311 }
312 }
313 }
314
315 /*
316 * Handle an incoming X event.
317 */
318 static void
319 runxevent(XEvent *xev)
320 {
321 int c;
322 KeySym k;
323 static Mouse m;
324 XButtonEvent *be;
325 XKeyEvent *ke;
326 Xwin *w;
327
328 #ifdef SHOWEVENT
329 static int first = 1;
330 if(first){
331 dup(create("/tmp/devdraw.out", OWRITE, 0666), 1);
332 setbuf(stdout, 0);
333 first = 0;
334 }
335 #endif
336
337 if(xev == 0)
338 return;
339
340 #ifdef SHOWEVENT
341 print("\n");
342 ShowEvent(xev);
343 #endif
344
345 w = nil;
346 switch(xev->type){
347 case Expose:
348 w = findxwin(((XExposeEvent*)xev)->window);
349 break;
350 case DestroyNotify:
351 w = findxwin(((XDestroyWindowEvent*)xev)->window);
352 break;
353 case ConfigureNotify:
354 w = findxwin(((XConfigureEvent*)xev)->window);
355 break;
356 case ButtonPress:
357 case ButtonRelease:
358 w = findxwin(((XButtonEvent*)xev)->window);
359 break;
360 case MotionNotify:
361 w = findxwin(((XMotionEvent*)xev)->window);
362 break;
363 case KeyRelease:
364 case KeyPress:
365 w = findxwin(((XKeyEvent*)xev)->window);
366 break;
367 case FocusOut:
368 w = findxwin(((XFocusChangeEvent*)xev)->window);
369 break;
370 }
371 if(w == nil)
372 w = _x.windows;
373
374 switch(xev->type){
375 case Expose:
376 _xexpose(w, xev);
377 break;
378
379 case DestroyNotify:
380 if(_xdestroy(w, xev))
381 threadexitsall(nil);
382 break;
383
384 case ConfigureNotify:
385 if(_xconfigure(w, xev))
386 _xreplacescreenimage(w->client);
387 break;
388
389 case ButtonPress:
390 be = (XButtonEvent*)xev;
391 if(be->button == 1) {
392 if(_x.kstate & ControlMask)
393 be->button = 2;
394 else if(_x.kstate & Mod1Mask)
395 be->button = 3;
396 }
397 // fall through
398 case ButtonRelease:
399 _x.altdown = 0;
400 // fall through
401 case MotionNotify:
402 if(_xtoplan9mouse(w, xev, &m) < 0)
403 return;
404 gfx_mousetrack(w->client, m.xy.x, m.xy.y, m.buttons|_x.kbuttons, m.msec);
405 break;
406
407 case KeyRelease:
408 case KeyPress:
409 ke = (XKeyEvent*)xev;
410 XLookupString(ke, NULL, 0, &k, NULL);
411 c = ke->state;
412 switch(k) {
413 case XK_Alt_L:
414 case XK_Meta_L: /* Shift Alt on PCs */
415 case XK_Alt_R:
416 case XK_Meta_R: /* Shift Alt on PCs */
417 case XK_Multi_key:
418 if(xev->type == KeyPress)
419 _x.altdown = 1;
420 else if(_x.altdown) {
421 _x.altdown = 0;
422 gfx_keystroke(w->client, Kalt);
423 }
424 break;
425 }
426
427 switch(k) {
428 case XK_Control_L:
429 if(xev->type == KeyPress)
430 c |= ControlMask;
431 else
432 c &= ~ControlMask;
433 goto kbutton;
434 case XK_Alt_L:
435 case XK_Shift_L:
436 if(xev->type == KeyPress)
437 c |= Mod1Mask;
438 else
439 c &= ~Mod1Mask;
440 kbutton:
441 _x.kstate = c;
442 if(m.buttons || _x.kbuttons) {
443 _x.altdown = 0; // used alt
444 _x.kbuttons = 0;
445 if(c & ControlMask)
446 _x.kbuttons |= 2;
447 if(c & Mod1Mask)
448 _x.kbuttons |= 4;
449 gfx_mousetrack(w->client, m.xy.x, m.xy.y, m.buttons|_x.kbuttons, m.msec);
450 break;
451 }
452 }
453
454 if(xev->type != KeyPress)
455 break;
456 if(k == XK_F11){
457 w->fullscreen = !w->fullscreen;
458 _xmovewindow(w, w->fullscreen ? w->screenrect : w->windowrect);
459 return;
460 }
461 if((c = _xtoplan9kbd(xev)) < 0)
462 return;
463 gfx_keystroke(w->client, c);
464 break;
465
466 case FocusOut:
467 /*
468 * Some key combinations (e.g. Alt-Tab) can cause us
469 * to see the key down event without the key up event,
470 * so clear out the keyboard state when we lose the focus.
471 */
472 _x.kstate = 0;
473 _x.altdown = 0;
474 gfx_abortcompose(w->client);
475 break;
476
477 case SelectionRequest:
478 _xselect(xev);
479 break;
480 }
481 }
482
483
484 static Memimage*
485 xattach(Client *client, char *label, char *winsize)
486 {
487 char *argv[2];
488 int havemin, height, mask, width, x, y;
489 Rectangle r;
490 XClassHint classhint;
491 XDrawable pmid;
492 XScreen *xscreen;
493 XSetWindowAttributes attr;
494 XSizeHints normalhint;
495 XTextProperty name;
496 XWindow xrootwin;
497 XWindowAttributes wattr;
498 XWMHints hint;
499 Atom atoms[2];
500 Xwin *w;
501
502 USED(client);
503 xscreen = DefaultScreenOfDisplay(_x.display);
504 xrootwin = DefaultRootWindow(_x.display);
505
506 /*
507 * We get to choose the initial rectangle size.
508 * This is arbitrary. In theory we should read the
509 * command line and allow the traditional X options.
510 */
511 mask = 0;
512 x = 0;
513 y = 0;
514 if(winsize && winsize[0]){
515 if(parsewinsize(winsize, &r, &havemin) < 0)
516 sysfatal("%r");
517 }else{
518 /*
519 * Parse the various X resources. Thanks to Peter Canning.
520 */
521 char *screen_resources, *display_resources, *geom,
522 *geomrestype, *home, *file, *dpitype;
523 XrmDatabase database;
524 XrmValue geomres, dpires;
525
526 database = XrmGetDatabase(_x.display);
527 screen_resources = XScreenResourceString(xscreen);
528 if(screen_resources != nil){
529 XrmCombineDatabase(XrmGetStringDatabase(screen_resources), &database, False);
530 XFree(screen_resources);
531 }
532
533 display_resources = XResourceManagerString(_x.display);
534 if(display_resources == nil){
535 home = getenv("HOME");
536 if(home!=nil && (file=smprint("%s/.Xdefaults", home)) != nil){
537 XrmCombineFileDatabase(file, &database, False);
538 free(file);
539 }
540 free(home);
541 }else
542 XrmCombineDatabase(XrmGetStringDatabase(display_resources), &database, False);
543
544 if (XrmGetResource(database, "Xft.dpi", "String", &dpitype, &dpires) == True) {
545 if (dpires.addr) {
546 client->displaydpi = atoi(dpires.addr);
547 }
548 }
549 geom = smprint("%s.geometry", label);
550 if(geom && XrmGetResource(database, geom, nil, &geomrestype, &geomres))
551 mask = XParseGeometry(geomres.addr, &x, &y, (uint*)&width, (uint*)&height);
552 XrmDestroyDatabase(database);
553 free(geom);
554
555 if((mask & WidthValue) && (mask & HeightValue)){
556 r = Rect(0, 0, width, height);
557 }else{
558 r = Rect(0, 0, WidthOfScreen(xscreen)*3/4,
559 HeightOfScreen(xscreen)*3/4);
560 if(Dx(r) > Dy(r)*3/2)
561 r.max.x = r.min.x + Dy(r)*3/2;
562 if(Dy(r) > Dx(r)*3/2)
563 r.max.y = r.min.y + Dx(r)*3/2;
564 }
565 if(mask & XNegative){
566 x += WidthOfScreen(xscreen);
567 }
568 if(mask & YNegative){
569 y += HeightOfScreen(xscreen);
570 }
571 havemin = 0;
572 }
573 w = newxwin(client);
574
575 memset(&attr, 0, sizeof attr);
576 attr.colormap = _x.cmap;
577 attr.background_pixel = ~0;
578 attr.border_pixel = 0;
579 w->drawable = XCreateWindow(
580 _x.display, /* display */
581 xrootwin, /* parent */
582 x, /* x */
583 y, /* y */
584 Dx(r), /* width */
585 Dy(r), /* height */
586 0, /* border width */
587 _x.depth, /* depth */
588 InputOutput, /* class */
589 _x.vis, /* visual */
590 /* valuemask */
591 CWBackPixel|CWBorderPixel|CWColormap,
592 &attr /* attributes (the above aren't?!) */
593 );
594
595 /*
596 * Label and other properties required by ICCCCM.
597 */
598 memset(&name, 0, sizeof name);
599 if(label == nil)
600 label = "pjw-face-here";
601 name.value = (uchar*)label;
602 name.encoding = XA_STRING;
603 name.format = 8;
604 name.nitems = strlen((char*)name.value);
605
606 memset(&normalhint, 0, sizeof normalhint);
607 normalhint.flags = PSize|PMaxSize;
608 if(winsize && winsize[0]){
609 normalhint.flags &= ~PSize;
610 normalhint.flags |= USSize;
611 normalhint.width = Dx(r);
612 normalhint.height = Dy(r);
613 }else{
614 if((mask & WidthValue) && (mask & HeightValue)){
615 normalhint.flags &= ~PSize;
616 normalhint.flags |= USSize;
617 normalhint.width = width;
618 normalhint.height = height;
619 }
620 if((mask & WidthValue) && (mask & HeightValue)){
621 normalhint.flags |= USPosition;
622 normalhint.x = x;
623 normalhint.y = y;
624 }
625 }
626
627 normalhint.max_width = WidthOfScreen(xscreen);
628 normalhint.max_height = HeightOfScreen(xscreen);
629
630 memset(&hint, 0, sizeof hint);
631 hint.flags = InputHint|StateHint;
632 hint.input = 1;
633 hint.initial_state = NormalState;
634
635 memset(&classhint, 0, sizeof classhint);
636 classhint.res_name = label;
637 classhint.res_class = label;
638
639 argv[0] = label;
640 argv[1] = nil;
641
642 XSetWMProperties(
643 _x.display, /* display */
644 w->drawable, /* window */
645 &name, /* XA_WM_NAME property */
646 &name, /* XA_WM_ICON_NAME property */
647 argv, /* XA_WM_COMMAND */
648 1, /* argc */
649 &normalhint, /* XA_WM_NORMAL_HINTS */
650 &hint, /* XA_WM_HINTS */
651 &classhint /* XA_WM_CLASSHINTS */
652 );
653 XFlush(_x.display);
654
655 if(havemin){
656 XWindowChanges ch;
657
658 memset(&ch, 0, sizeof ch);
659 ch.x = r.min.x;
660 ch.y = r.min.y;
661 XConfigureWindow(_x.display, w->drawable, CWX|CWY, &ch);
662 /*
663 * Must pretend origin is 0,0 for X.
664 */
665 r = Rect(0,0,Dx(r),Dy(r));
666 }
667 /*
668 * Look up clipboard atom.
669 */
670 if(_x.clipboard == 0) {
671 _x.clipboard = XInternAtom(_x.display, "CLIPBOARD", False);
672 _x.utf8string = XInternAtom(_x.display, "UTF8_STRING", False);
673 _x.targets = XInternAtom(_x.display, "TARGETS", False);
674 _x.text = XInternAtom(_x.display, "TEXT", False);
675 _x.compoundtext = XInternAtom(_x.display, "COMPOUND_TEXT", False);
676 _x.takefocus = XInternAtom(_x.display, "WM_TAKE_FOCUS", False);
677 _x.losefocus = XInternAtom(_x.display, "_9WM_LOSE_FOCUS", False);
678 _x.wmprotos = XInternAtom(_x.display, "WM_PROTOCOLS", False);
679 }
680
681 atoms[0] = _x.takefocus;
682 atoms[1] = _x.losefocus;
683 XChangeProperty(_x.display, w->drawable, _x.wmprotos, XA_ATOM, 32,
684 PropModeReplace, (uchar*)atoms, 2);
685
686 /*
687 * Put the window on the screen, check to see what size we actually got.
688 */
689 XMapWindow(_x.display, w->drawable);
690 XSync(_x.display, False);
691
692 if(!XGetWindowAttributes(_x.display, w->drawable, &wattr))
693 fprint(2, "XGetWindowAttributes failed\n");
694 else if(wattr.width && wattr.height){
695 if(wattr.width != Dx(r) || wattr.height != Dy(r)){
696 r.max.x = wattr.width;
697 r.max.y = wattr.height;
698 }
699 }else
700 fprint(2, "XGetWindowAttributes: bad attrs\n");
701 w->screenrect = Rect(0, 0, WidthOfScreen(xscreen), HeightOfScreen(xscreen));
702 w->windowrect = r;
703
704 /*
705 * Allocate our local backing store.
706 */
707 w->screenr = r;
708 w->screenpm = XCreatePixmap(_x.display, w->drawable, Dx(r), Dy(r), _x.depth);
709 w->nextscreenpm = w->screenpm;
710 w->screenimage = _xallocmemimage(r, _x.chan, w->screenpm);
711 client->mouserect = r;
712
713 /*
714 * Allocate some useful graphics contexts for the future.
715 * These can be used with any drawable matching w->drawable's
716 * pixel format (which is all the drawables we create).
717 */
718 if(_x.gcfill == 0) {
719 _x.gcfill = xgc(w->screenpm, FillSolid, -1);
720 _x.gccopy = xgc(w->screenpm, -1, -1);
721 _x.gcsimplesrc = xgc(w->screenpm, FillStippled, -1);
722 _x.gczero = xgc(w->screenpm, -1, -1);
723 _x.gcreplsrc = xgc(w->screenpm, FillTiled, -1);
724
725 pmid = XCreatePixmap(_x.display, w->drawable, 1, 1, 1);
726 _x.gcfill0 = xgc(pmid, FillSolid, 0);
727 _x.gccopy0 = xgc(pmid, -1, -1);
728 _x.gcsimplesrc0 = xgc(pmid, FillStippled, -1);
729 _x.gczero0 = xgc(pmid, -1, -1);
730 _x.gcreplsrc0 = xgc(pmid, FillTiled, -1);
731 XFreePixmap(_x.display, pmid);
732 }
733
734 return w->screenimage;
735 }
736
737 Memimage*
738 rpc_attach(Client *client, char *label, char *winsize)
739 {
740 Memimage *m;
741
742 xlock();
743 m = xattach(client, label, winsize);
744 xunlock();
745 return m;
746 }
747
748 void
749 rpc_setlabel(Client *client, char *label)
750 {
751 Xwin *w = (Xwin*)client->view;
752 XTextProperty name;
753
754 /*
755 * Label and other properties required by ICCCCM.
756 */
757 xlock();
758 memset(&name, 0, sizeof name);
759 if(label == nil)
760 label = "pjw-face-here";
761 name.value = (uchar*)label;
762 name.encoding = XA_STRING;
763 name.format = 8;
764 name.nitems = strlen((char*)name.value);
765
766 XSetWMProperties(
767 _x.display, /* display */
768 w->drawable, /* window */
769 &name, /* XA_WM_NAME property */
770 &name, /* XA_WM_ICON_NAME property */
771 nil, /* XA_WM_COMMAND */
772 0, /* argc */
773 nil, /* XA_WM_NORMAL_HINTS */
774 nil, /* XA_WM_HINTS */
775 nil /* XA_WM_CLASSHINTS */
776 );
777 XFlush(_x.display);
778 xunlock();
779 }
780
781 /*
782 * Create a GC with a particular fill style and XXX.
783 * Disable generation of GraphicsExpose/NoExpose events in the GC.
784 */
785 static XGC
786 xgc(XDrawable d, int fillstyle, int foreground)
787 {
788 XGC gc;
789 XGCValues v;
790
791 memset(&v, 0, sizeof v);
792 v.function = GXcopy;
793 v.graphics_exposures = False;
794 gc = XCreateGC(_x.display, d, GCFunction|GCGraphicsExposures, &v);
795 if(fillstyle != -1)
796 XSetFillStyle(_x.display, gc, fillstyle);
797 if(foreground != -1)
798 XSetForeground(_x.display, gc, 0);
799 return gc;
800 }
801
802
803 /*
804 * Initialize map with the Plan 9 rgbv color map.
805 */
806 static void
807 plan9cmap(void)
808 {
809 int r, g, b, cr, cg, cb, v, num, den, idx, v7, idx7;
810 static int once;
811
812 if(once)
813 return;
814 once = 1;
815
816 for(r=0; r!=4; r++)
817 for(g = 0; g != 4; g++)
818 for(b = 0; b!=4; b++)
819 for(v = 0; v!=4; v++){
820 den=r;
821 if(g > den)
822 den=g;
823 if(b > den)
824 den=b;
825 /* divide check -- pick grey shades */
826 if(den==0)
827 cr=cg=cb=v*17;
828 else {
829 num=17*(4*den+v);
830 cr=r*num/den;
831 cg=g*num/den;
832 cb=b*num/den;
833 }
834 idx = r*64 + v*16 + ((g*4 + b + v - r) & 15);
835 _x.map[idx].red = cr*0x0101;
836 _x.map[idx].green = cg*0x0101;
837 _x.map[idx].blue = cb*0x0101;
838 _x.map[idx].pixel = idx;
839 _x.map[idx].flags = DoRed|DoGreen|DoBlue;
840
841 v7 = v >> 1;
842 idx7 = r*32 + v7*16 + g*4 + b;
843 if((v & 1) == v7){
844 _x.map7to8[idx7][0] = idx;
845 if(den == 0) { /* divide check -- pick grey shades */
846 cr = ((255.0/7.0)*v7)+0.5;
847 cg = cr;
848 cb = cr;
849 }
850 else {
851 num=17*15*(4*den+v7*2)/14;
852 cr=r*num/den;
853 cg=g*num/den;
854 cb=b*num/den;
855 }
856 _x.map7[idx7].red = cr*0x0101;
857 _x.map7[idx7].green = cg*0x0101;
858 _x.map7[idx7].blue = cb*0x0101;
859 _x.map7[idx7].pixel = idx7;
860 _x.map7[idx7].flags = DoRed|DoGreen|DoBlue;
861 }
862 else
863 _x.map7to8[idx7][1] = idx;
864 }
865 }
866
867 /*
868 * Initialize and install the rgbv color map as a private color map
869 * for this application. It gets the best colors when it has the
870 * cursor focus.
871 *
872 * We always choose the best depth possible, but that might not
873 * be the default depth. On such "suboptimal" systems, we have to allocate an
874 * empty color map anyway, according to Axel Belinfante.
875 */
876 static int
877 setupcmap(XWindow w)
878 {
879 char buf[30];
880 int i;
881 u32int p, pp;
882 XColor c;
883
884 if(_x.depth <= 1)
885 return 0;
886
887 if(_x.depth >= 24) {
888 if(_x.usetable == 0)
889 _x.cmap = XCreateColormap(_x.display, w, _x.vis, AllocNone);
890
891 /*
892 * The pixel value returned from XGetPixel needs to
893 * be converted to RGB so we can call rgb2cmap()
894 * to translate between 24 bit X and our color. Unfortunately,
895 * the return value appears to be display server endian
896 * dependant. Therefore, we run some heuristics to later
897 * determine how to mask the int value correctly.
898 * Yeah, I know we can look at _x.vis->byte_order but
899 * some displays say MSB even though they run on LSB.
900 * Besides, this is more anal.
901 */
902 c = _x.map[19]; /* known to have different R, G, B values */
903 if(!XAllocColor(_x.display, _x.cmap, &c)){
904 werrstr("XAllocColor: %r");
905 return -1;
906 }
907 p = c.pixel;
908 pp = rgb2cmap((p>>16)&0xff,(p>>8)&0xff,p&0xff);
909 if(pp != _x.map[19].pixel) {
910 /* check if endian is other way */
911 pp = rgb2cmap(p&0xff,(p>>8)&0xff,(p>>16)&0xff);
912 if(pp != _x.map[19].pixel){
913 werrstr("cannot detect X server byte order");
914 return -1;
915 }
916
917 switch(_x.chan){
918 case RGB24:
919 _x.chan = BGR24;
920 break;
921 case XRGB32:
922 _x.chan = XBGR32;
923 break;
924 default:
925 werrstr("cannot byteswap channel %s",
926 chantostr(buf, _x.chan));
927 break;
928 }
929 }
930 }else if(_x.vis->class == TrueColor || _x.vis->class == DirectColor){
931 /*
932 * Do nothing. We have no way to express a
933 * mixed-endian 16-bit screen, so pretend they don't exist.
934 */
935 if(_x.usetable == 0)
936 _x.cmap = XCreateColormap(_x.display, w, _x.vis, AllocNone);
937 }else if(_x.vis->class == PseudoColor){
938 if(_x.usetable == 0){
939 _x.cmap = XCreateColormap(_x.display, w, _x.vis, AllocAll);
940 XStoreColors(_x.display, _x.cmap, _x.map, 256);
941 for(i = 0; i < 256; i++){
942 _x.tox11[i] = i;
943 _x.toplan9[i] = i;
944 }
945 }else{
946 for(i = 0; i < 128; i++){
947 c = _x.map7[i];
948 if(!XAllocColor(_x.display, _x.cmap, &c)){
949 werrstr("can't allocate colors in 7-bit map");
950 return -1;
951 }
952 _x.tox11[_x.map7to8[i][0]] = c.pixel;
953 _x.tox11[_x.map7to8[i][1]] = c.pixel;
954 _x.toplan9[c.pixel] = _x.map7to8[i][0];
955 }
956 }
957 }else{
958 werrstr("unsupported visual class %d", _x.vis->class);
959 return -1;
960 }
961 return 0;
962 }
963
964 void
965 rpc_shutdown(void)
966 {
967 }
968
969 void
970 rpc_flush(Client *client, Rectangle r)
971 {
972 Xwin *w = (Xwin*)client->view;
973
974 xlock();
975 if(w->nextscreenpm != w->screenpm){
976 XSync(_x.display, False);
977 XFreePixmap(_x.display, w->screenpm);
978 w->screenpm = w->nextscreenpm;
979 }
980
981 if(r.min.x >= r.max.x || r.min.y >= r.max.y) {
982 xunlock();
983 return;
984 }
985
986 XCopyArea(_x.display, w->screenpm, w->drawable, _x.gccopy, r.min.x, r.min.y,
987 Dx(r), Dy(r), r.min.x, r.min.y);
988 XFlush(_x.display);
989 xunlock();
990 }
991
992 static void
993 _xexpose(Xwin *w, XEvent *e)
994 {
995 XExposeEvent *xe;
996 Rectangle r;
997
998 if(w->screenpm != w->nextscreenpm)
999 return;
1000 xe = (XExposeEvent*)e;
1001 r.min.x = xe->x;
1002 r.min.y = xe->y;
1003 r.max.x = xe->x+xe->width;
1004 r.max.y = xe->y+xe->height;
1005 XCopyArea(_x.display, w->screenpm, w->drawable, _x.gccopy, r.min.x, r.min.y,
1006 Dx(r), Dy(r), r.min.x, r.min.y);
1007 XSync(_x.display, False);
1008 }
1009
1010 static int
1011 _xdestroy(Xwin *w, XEvent *e)
1012 {
1013 XDestroyWindowEvent *xe;
1014
1015 xe = (XDestroyWindowEvent*)e;
1016 if(xe->window == w->drawable){
1017 w->destroyed = 1;
1018 return 1;
1019 }
1020 return 0;
1021 }
1022
1023 static int
1024 _xconfigure(Xwin *w, XEvent *e)
1025 {
1026 Rectangle r;
1027 XConfigureEvent *xe = (XConfigureEvent*)e;
1028
1029 if(!w->fullscreen){
1030 int rx, ry;
1031 XWindow xw;
1032 if(XTranslateCoordinates(_x.display, w->drawable, DefaultRootWindow(_x.display), 0, 0, &rx, &ry, &xw))
1033 w->windowrect = Rect(rx, ry, rx+xe->width, ry+xe->height);
1034 }
1035
1036 if(xe->width == Dx(w->screenr) && xe->height == Dy(w->screenr))
1037 return 0;
1038 r = Rect(0, 0, xe->width, xe->height);
1039
1040 if(w->screenpm != w->nextscreenpm){
1041 XCopyArea(_x.display, w->screenpm, w->drawable, _x.gccopy, r.min.x, r.min.y,
1042 Dx(r), Dy(r), r.min.x, r.min.y);
1043 XSync(_x.display, False);
1044 }
1045 w->newscreenr = r;
1046 return 1;
1047 }
1048
1049 static int
1050 _xreplacescreenimage(Client *client)
1051 {
1052 Memimage *m;
1053 XDrawable pixmap;
1054 Rectangle r;
1055 Xwin *w;
1056
1057 w = (Xwin*)client->view;
1058 r = w->newscreenr;
1059 pixmap = XCreatePixmap(_x.display, w->drawable, Dx(r), Dy(r), _x.depth);
1060 m = _xallocmemimage(r, _x.chan, pixmap);
1061 if(w->nextscreenpm != w->screenpm)
1062 XFreePixmap(_x.display, w->nextscreenpm);
1063 w->nextscreenpm = pixmap;
1064 w->screenr = r;
1065 client->mouserect = r;
1066 xunlock();
1067 gfx_replacescreenimage(client, m);
1068 xlock();
1069 return 1;
1070 }
1071
1072 void
1073 rpc_resizeimg(Client *client)
1074 {
1075 xlock();
1076 _xreplacescreenimage(client);
1077 xunlock();
1078 }
1079
1080 void
1081 rpc_gfxdrawlock(void)
1082 {
1083 xlock();
1084 }
1085
1086 void
1087 rpc_gfxdrawunlock(void)
1088 {
1089 xunlock();
1090 }
1091 void
1092 rpc_topwin(Client *client)
1093 {
1094 Xwin *w = (Xwin*)client->view;
1095
1096 xlock();
1097 XMapRaised(_x.display, w->drawable);
1098 XSetInputFocus(_x.display, w->drawable, RevertToPointerRoot,
1099 CurrentTime);
1100 XFlush(_x.display);
1101 xunlock();
1102 }
1103
1104 void
1105 rpc_resizewindow(Client *client, Rectangle r)
1106 {
1107 Xwin *w = (Xwin*)client->view;
1108 XWindowChanges e;
1109 int value_mask;
1110
1111 xlock();
1112 memset(&e, 0, sizeof e);
1113 value_mask = CWX|CWY|CWWidth|CWHeight;
1114 e.width = Dx(r);
1115 e.height = Dy(r);
1116 XConfigureWindow(_x.display, w->drawable, value_mask, &e);
1117 XFlush(_x.display);
1118 xunlock();
1119 }
1120
1121 static void
1122 _xmovewindow(Xwin *w, Rectangle r)
1123 {
1124 XWindowChanges e;
1125 int value_mask;
1126
1127 memset(&e, 0, sizeof e);
1128 value_mask = CWX|CWY|CWWidth|CWHeight;
1129 e.x = r.min.x;
1130 e.y = r.min.y;
1131 e.width = Dx(r);
1132 e.height = Dy(r);
1133 XConfigureWindow(_x.display, w->drawable, value_mask, &e);
1134 XFlush(_x.display);
1135 }
1136
1137 static int
1138 _xtoplan9kbd(XEvent *e)
1139 {
1140 KeySym k;
1141
1142 if(e->xany.type != KeyPress)
1143 return -1;
1144 needstack(64*1024); /* X has some *huge* buffers in openobject */
1145 /* and they're even bigger on SuSE */
1146 XLookupString((XKeyEvent*)e,NULL,0,&k,NULL);
1147 if(k == NoSymbol)
1148 return -1;
1149
1150 if(k&0xFF00){
1151 switch(k){
1152 case XK_BackSpace:
1153 case XK_Tab:
1154 case XK_Escape:
1155 case XK_Delete:
1156 case XK_KP_0:
1157 case XK_KP_1:
1158 case XK_KP_2:
1159 case XK_KP_3:
1160 case XK_KP_4:
1161 case XK_KP_5:
1162 case XK_KP_6:
1163 case XK_KP_7:
1164 case XK_KP_8:
1165 case XK_KP_9:
1166 case XK_KP_Divide:
1167 case XK_KP_Multiply:
1168 case XK_KP_Subtract:
1169 case XK_KP_Add:
1170 case XK_KP_Decimal:
1171 k &= 0x7F;
1172 break;
1173 case XK_Linefeed:
1174 k = '\r';
1175 break;
1176 case XK_KP_Space:
1177 k = ' ';
1178 break;
1179 case XK_Home:
1180 case XK_KP_Home:
1181 k = Khome;
1182 break;
1183 case XK_Left:
1184 case XK_KP_Left:
1185 k = Kleft;
1186 break;
1187 case XK_Up:
1188 case XK_KP_Up:
1189 k = Kup;
1190 break;
1191 case XK_Down:
1192 case XK_KP_Down:
1193 k = Kdown;
1194 break;
1195 case XK_Right:
1196 case XK_KP_Right:
1197 k = Kright;
1198 break;
1199 case XK_Page_Down:
1200 case XK_KP_Page_Down:
1201 k = Kpgdown;
1202 break;
1203 case XK_End:
1204 case XK_KP_End:
1205 k = Kend;
1206 break;
1207 case XK_Page_Up:
1208 case XK_KP_Page_Up:
1209 k = Kpgup;
1210 break;
1211 case XK_Insert:
1212 case XK_KP_Insert:
1213 k = Kins;
1214 break;
1215 case XK_KP_Enter:
1216 case XK_Return:
1217 k = '\n';
1218 break;
1219 case XK_Alt_L:
1220 case XK_Meta_L: /* Shift Alt on PCs */
1221 case XK_Alt_R:
1222 case XK_Meta_R: /* Shift Alt on PCs */
1223 case XK_Multi_key:
1224 return -1;
1225 default: /* not ISO-1 or tty control */
1226 if(k>0xff) {
1227 k = _p9keysym2ucs(k);
1228 if(k==-1) return -1;
1229 }
1230 }
1231 }
1232
1233 /* Compensate for servers that call a minus a hyphen */
1234 if(k == XK_hyphen)
1235 k = XK_minus;
1236 /* Do control mapping ourselves if translator doesn't */
1237 if(e->xkey.state&ControlMask)
1238 k &= 0x9f;
1239 if(k == NoSymbol) {
1240 return -1;
1241 }
1242
1243 return k+0;
1244 }
1245
1246 static int
1247 _xtoplan9mouse(Xwin *w, XEvent *e, Mouse *m)
1248 {
1249 int s;
1250 XButtonEvent *be;
1251 XMotionEvent *me;
1252
1253 if(_x.putsnarf != _x.assertsnarf){
1254 _x.assertsnarf = _x.putsnarf;
1255 XSetSelectionOwner(_x.display, XA_PRIMARY, w->drawable, CurrentTime);
1256 if(_x.clipboard != None)
1257 XSetSelectionOwner(_x.display, _x.clipboard, w->drawable, CurrentTime);
1258 XFlush(_x.display);
1259 }
1260
1261 switch(e->type){
1262 case ButtonPress:
1263 be = (XButtonEvent*)e;
1264
1265 /*
1266 * Fake message, just sent to make us announce snarf.
1267 * Apparently state and button are 16 and 8 bits on
1268 * the wire, since they are truncated by the time they
1269 * get to us.
1270 */
1271 if(be->send_event
1272 && (~be->state&0xFFFF)==0
1273 && (~be->button&0xFF)==0)
1274 return -1;
1275 /* BUG? on mac need to inherit these from elsewhere? */
1276 m->xy.x = be->x;
1277 m->xy.y = be->y;
1278 s = be->state;
1279 m->msec = be->time;
1280 switch(be->button){
1281 case 1:
1282 s |= Button1Mask;
1283 break;
1284 case 2:
1285 s |= Button2Mask;
1286 break;
1287 case 3:
1288 s |= Button3Mask;
1289 break;
1290 case 4:
1291 s |= Button4Mask;
1292 break;
1293 case 5:
1294 s |= Button5Mask;
1295 break;
1296 }
1297 break;
1298 case ButtonRelease:
1299 be = (XButtonEvent*)e;
1300 m->xy.x = be->x;
1301 m->xy.y = be->y;
1302 s = be->state;
1303 m->msec = be->time;
1304 switch(be->button){
1305 case 1:
1306 s &= ~Button1Mask;
1307 break;
1308 case 2:
1309 s &= ~Button2Mask;
1310 break;
1311 case 3:
1312 s &= ~Button3Mask;
1313 break;
1314 case 4:
1315 s &= ~Button4Mask;
1316 break;
1317 case 5:
1318 s &= ~Button5Mask;
1319 break;
1320 }
1321 break;
1322
1323 case MotionNotify:
1324 me = (XMotionEvent*)e;
1325 s = me->state;
1326 m->xy.x = me->x;
1327 m->xy.y = me->y;
1328 m->msec = me->time;
1329 return 0; // do not set buttons
1330
1331 default:
1332 return -1;
1333 }
1334
1335 m->buttons = 0;
1336 if(s & Button1Mask)
1337 m->buttons |= 1;
1338 if(s & Button2Mask)
1339 m->buttons |= 2;
1340 if(s & Button3Mask)
1341 m->buttons |= 4;
1342 if(s & Button4Mask)
1343 m->buttons |= 8;
1344 if(s & Button5Mask)
1345 m->buttons |= 16;
1346 return 0;
1347 }
1348
1349 void
1350 rpc_setmouse(Client *client, Point p)
1351 {
1352 Xwin *w = (Xwin*)client->view;
1353
1354 xlock();
1355 XWarpPointer(_x.display, None, w->drawable, 0, 0, 0, 0, p.x, p.y);
1356 XFlush(_x.display);
1357 xunlock();
1358 }
1359
1360 static int
1361 revbyte(int b)
1362 {
1363 int r;
1364
1365 r = 0;
1366 r |= (b&0x01) << 7;
1367 r |= (b&0x02) << 5;
1368 r |= (b&0x04) << 3;
1369 r |= (b&0x08) << 1;
1370 r |= (b&0x10) >> 1;
1371 r |= (b&0x20) >> 3;
1372 r |= (b&0x40) >> 5;
1373 r |= (b&0x80) >> 7;
1374 return r;
1375 }
1376
1377 static void
1378 xcursorarrow(Xwin *w)
1379 {
1380 if(_x.cursor != 0){
1381 XFreeCursor(_x.display, _x.cursor);
1382 _x.cursor = 0;
1383 }
1384 XUndefineCursor(_x.display, w->drawable);
1385 XFlush(_x.display);
1386 }
1387
1388
1389 void
1390 rpc_setcursor(Client *client, Cursor *c, Cursor2 *c2)
1391 {
1392 Xwin *w = (Xwin*)client->view;
1393 XColor fg, bg;
1394 XCursor xc;
1395 Pixmap xsrc, xmask;
1396 int i;
1397 uchar src[2*16], mask[2*16];
1398
1399 USED(c2);
1400
1401 xlock();
1402 if(c == nil){
1403 xcursorarrow(w);
1404 xunlock();
1405 return;
1406 }
1407 for(i=0; i<2*16; i++){
1408 src[i] = revbyte(c->set[i]);
1409 mask[i] = revbyte(c->set[i] | c->clr[i]);
1410 }
1411
1412 fg = _x.map[0];
1413 bg = _x.map[255];
1414 xsrc = XCreateBitmapFromData(_x.display, w->drawable, (char*)src, 16, 16);
1415 xmask = XCreateBitmapFromData(_x.display, w->drawable, (char*)mask, 16, 16);
1416 xc = XCreatePixmapCursor(_x.display, xsrc, xmask, &fg, &bg, -c->offset.x, -c->offset.y);
1417 if(xc != 0) {
1418 XDefineCursor(_x.display, w->drawable, xc);
1419 if(_x.cursor != 0)
1420 XFreeCursor(_x.display, _x.cursor);
1421 _x.cursor = xc;
1422 }
1423 XFreePixmap(_x.display, xsrc);
1424 XFreePixmap(_x.display, xmask);
1425 XFlush(_x.display);
1426 xunlock();
1427 }
1428
1429 struct {
1430 QLock lk;
1431 char buf[SnarfSize];
1432 #ifdef APPLESNARF
1433 Rune rbuf[SnarfSize];
1434 PasteboardRef apple;
1435 #endif
1436 } clip;
1437
1438 static uchar*
1439 _xgetsnarffrom(Xwin *w, XWindow xw, Atom clipboard, Atom target, int timeout0, int timeout)
1440 {
1441 Atom prop, type;
1442 ulong len, lastlen, dummy;
1443 int fmt, i;
1444 uchar *data, *xdata;
1445
1446 /*
1447 * We should be waiting for SelectionNotify here, but it might never
1448 * come, and we have no way to time out. Instead, we will clear
1449 * local property #1, request our buddy to fill it in for us, and poll
1450 * until he's done or we get tired of waiting.
1451 */
1452 prop = 1;
1453 XChangeProperty(_x.display, w->drawable, prop, target, 8, PropModeReplace, (uchar*)"", 0);
1454 XConvertSelection(_x.display, clipboard, target, prop, w->drawable, CurrentTime);
1455 XFlush(_x.display);
1456 lastlen = 0;
1457 timeout0 = (timeout0 + 9)/10;
1458 timeout = (timeout + 9)/10;
1459 for(i=0; i<timeout0 || (lastlen!=0 && i<timeout); i++){
1460 usleep(10*1000);
1461 XGetWindowProperty(_x.display, w->drawable, prop, 0, 0, 0, AnyPropertyType,
1462 &type, &fmt, &dummy, &len, &xdata);
1463 if(lastlen == len && len > 0){
1464 XFree(xdata);
1465 break;
1466 }
1467 lastlen = len;
1468 XFree(xdata);
1469 }
1470 if(len == 0)
1471 return nil;
1472
1473 /* get the property */
1474 xdata = nil;
1475 XGetWindowProperty(_x.display, w->drawable, prop, 0, SnarfSize/sizeof(ulong), 0,
1476 AnyPropertyType, &type, &fmt, &len, &dummy, &xdata);
1477 if((type != target && type != XA_STRING && type != _x.utf8string) || len == 0){
1478 if(xdata)
1479 XFree(xdata);
1480 return nil;
1481 }
1482 if(xdata){
1483 data = (uchar*)strdup((char*)xdata);
1484 XFree(xdata);
1485 return data;
1486 }
1487 return nil;
1488 }
1489
1490 char*
1491 rpc_getsnarf(void)
1492 {
1493 uchar *data;
1494 Atom clipboard;
1495 XWindow xw;
1496 Xwin *w;
1497
1498 qlock(&clip.lk);
1499 xlock();
1500 w = _x.windows;
1501 /*
1502 * Have we snarfed recently and the X server hasn't caught up?
1503 */
1504 if(_x.putsnarf != _x.assertsnarf)
1505 goto mine;
1506
1507 /*
1508 * Is there a primary selection (highlighted text in an xterm)?
1509 */
1510 clipboard = XA_PRIMARY;
1511 xw = XGetSelectionOwner(_x.display, XA_PRIMARY);
1512 // TODO check more
1513 if(xw == w->drawable){
1514 mine:
1515 data = (uchar*)strdup(clip.buf);
1516 goto out;
1517 }
1518
1519 /*
1520 * If not, is there a clipboard selection?
1521 */
1522 if(xw == None && _x.clipboard != None){
1523 clipboard = _x.clipboard;
1524 xw = XGetSelectionOwner(_x.display, _x.clipboard);
1525 if(xw == w->drawable)
1526 goto mine;
1527 }
1528
1529 /*
1530 * If not, give up.
1531 */
1532 if(xw == None){
1533 data = nil;
1534 goto out;
1535 }
1536
1537 if((data = _xgetsnarffrom(w, xw, clipboard, _x.utf8string, 10, 100)) == nil)
1538 if((data = _xgetsnarffrom(w, xw, clipboard, XA_STRING, 10, 100)) == nil){
1539 /* nothing left to do */
1540 }
1541
1542 out:
1543 xunlock();
1544 qunlock(&clip.lk);
1545 return (char*)data;
1546 }
1547
1548 void
1549 __xputsnarf(char *data)
1550 {
1551 XButtonEvent e;
1552 Xwin *w;
1553
1554 if(strlen(data) >= SnarfSize)
1555 return;
1556 qlock(&clip.lk);
1557 xlock();
1558 w = _x.windows;
1559 strcpy(clip.buf, data);
1560 /* leave note for mouse proc to assert selection ownership */
1561 _x.putsnarf++;
1562
1563 /* send mouse a fake event so snarf is announced */
1564 memset(&e, 0, sizeof e);
1565 e.type = ButtonPress;
1566 e.window = w->drawable;
1567 e.state = ~0;
1568 e.button = ~0;
1569 XSendEvent(_x.display, w->drawable, True, ButtonPressMask, (XEvent*)&e);
1570 XFlush(_x.display);
1571 xunlock();
1572 qunlock(&clip.lk);
1573 }
1574
1575 static int
1576 _xselect(XEvent *e)
1577 {
1578 char *name;
1579 XEvent r;
1580 XSelectionRequestEvent *xe;
1581 Atom a[4];
1582
1583 memset(&r, 0, sizeof r);
1584 xe = (XSelectionRequestEvent*)e;
1585 if(0) fprint(2, "xselect target=%d requestor=%d property=%d selection=%d (sizeof atom=%d)\n",
1586 xe->target, xe->requestor, xe->property, xe->selection, sizeof a[0]);
1587 r.xselection.property = xe->property;
1588 if(xe->target == _x.targets){
1589 a[0] = _x.utf8string;
1590 a[1] = XA_STRING;
1591 a[2] = _x.text;
1592 a[3] = _x.compoundtext;
1593 XChangeProperty(_x.display, xe->requestor, xe->property, XA_ATOM,
1594 32, PropModeReplace, (uchar*)a, nelem(a));
1595 }else if(xe->target == XA_STRING
1596 || xe->target == _x.utf8string
1597 || xe->target == _x.text
1598 || xe->target == _x.compoundtext
1599 || ((name = XGetAtomName(_x.display, xe->target)) && strcmp(name, "text/plain;charset=UTF-8") == 0)){
1600 /* text/plain;charset=UTF-8 seems nonstandard but is used by Synergy */
1601 /* if the target is STRING we're supposed to reply with Latin1 XXX */
1602 qlock(&clip.lk);
1603 XChangeProperty(_x.display, xe->requestor, xe->property, xe->target,
1604 8, PropModeReplace, (uchar*)clip.buf, strlen(clip.buf));
1605 qunlock(&clip.lk);
1606 }else{
1607 if(strcmp(name, "TIMESTAMP") != 0)
1608 fprint(2, "%s: cannot handle selection request for '%s' (%d)\n", argv0, name, (int)xe->target);
1609 r.xselection.property = None;
1610 }
1611
1612 r.xselection.display = xe->display;
1613 /* r.xselection.property filled above */
1614 r.xselection.target = xe->target;
1615 r.xselection.type = SelectionNotify;
1616 r.xselection.requestor = xe->requestor;
1617 r.xselection.time = xe->time;
1618 r.xselection.send_event = True;
1619 r.xselection.selection = xe->selection;
1620 XSendEvent(_x.display, xe->requestor, False, 0, &r);
1621 XFlush(_x.display);
1622 return 0;
1623 }
1624
1625 #ifdef APPLESNARF
1626 char*
1627 _applegetsnarf(void)
1628 {
1629 char *s, *t;
1630 CFArrayRef flavors;
1631 CFDataRef data;
1632 CFIndex nflavor, ndata, j;
1633 CFStringRef type;
1634 ItemCount nitem;
1635 PasteboardItemID id;
1636 PasteboardSyncFlags flags;
1637 UInt32 i;
1638
1639 /* fprint(2, "applegetsnarf\n"); */
1640 qlock(&clip.lk);
1641 if(clip.apple == nil){
1642 if(PasteboardCreate(kPasteboardClipboard, &clip.apple) != noErr){
1643 fprint(2, "apple pasteboard create failed\n");
1644 qunlock(&clip.lk);
1645 return nil;
1646 }
1647 }
1648 flags = PasteboardSynchronize(clip.apple);
1649 if(flags&kPasteboardClientIsOwner){
1650 s = strdup(clip.buf);
1651 qunlock(&clip.lk);
1652 return s;
1653 }
1654 if(PasteboardGetItemCount(clip.apple, &nitem) != noErr){
1655 fprint(2, "apple pasteboard get item count failed\n");
1656 qunlock(&clip.lk);
1657 return nil;
1658 }
1659 for(i=1; i<=nitem; i++){
1660 if(PasteboardGetItemIdentifier(clip.apple, i, &id) != noErr)
1661 continue;
1662 if(PasteboardCopyItemFlavors(clip.apple, id, &flavors) != noErr)
1663 continue;
1664 nflavor = CFArrayGetCount(flavors);
1665 for(j=0; j<nflavor; j++){
1666 type = (CFStringRef)CFArrayGetValueAtIndex(flavors, j);
1667 if(!UTTypeConformsTo(type, CFSTR("public.utf16-plain-text")))
1668 continue;
1669 if(PasteboardCopyItemFlavorData(clip.apple, id, type, &data) != noErr)
1670 continue;
1671 ndata = CFDataGetLength(data);
1672 qunlock(&clip.lk);
1673 s = smprint("%.*S", ndata/2, (Rune*)CFDataGetBytePtr(data));
1674 CFRelease(flavors);
1675 CFRelease(data);
1676 for(t=s; *t; t++)
1677 if(*t == '\r')
1678 *t = '\n';
1679 return s;
1680 }
1681 CFRelease(flavors);
1682 }
1683 qunlock(&clip.lk);
1684 return nil;
1685 }
1686
1687 void
1688 _appleputsnarf(char *s)
1689 {
1690 CFDataRef cfdata;
1691 PasteboardSyncFlags flags;
1692
1693 /* fprint(2, "appleputsnarf\n"); */
1694
1695 if(strlen(s) >= SnarfSize)
1696 return;
1697 qlock(&clip.lk);
1698 strcpy(clip.buf, s);
1699 runesnprint(clip.rbuf, nelem(clip.rbuf), "%s", s);
1700 if(clip.apple == nil){
1701 if(PasteboardCreate(kPasteboardClipboard, &clip.apple) != noErr){
1702 fprint(2, "apple pasteboard create failed\n");
1703 qunlock(&clip.lk);
1704 return;
1705 }
1706 }
1707 if(PasteboardClear(clip.apple) != noErr){
1708 fprint(2, "apple pasteboard clear failed\n");
1709 qunlock(&clip.lk);
1710 return;
1711 }
1712 flags = PasteboardSynchronize(clip.apple);
1713 if((flags&kPasteboardModified) || !(flags&kPasteboardClientIsOwner)){
1714 fprint(2, "apple pasteboard cannot assert ownership\n");
1715 qunlock(&clip.lk);
1716 return;
1717 }
1718 cfdata = CFDataCreate(kCFAllocatorDefault,
1719 (uchar*)clip.rbuf, runestrlen(clip.rbuf)*2);
1720 if(cfdata == nil){
1721 fprint(2, "apple pasteboard cfdatacreate failed\n");
1722 qunlock(&clip.lk);
1723 return;
1724 }
1725 if(PasteboardPutItemFlavor(clip.apple, (PasteboardItemID)1,
1726 CFSTR("public.utf16-plain-text"), cfdata, 0) != noErr){
1727 fprint(2, "apple pasteboard putitem failed\n");
1728 CFRelease(cfdata);
1729 qunlock(&clip.lk);
1730 return;
1731 }
1732 /* CFRelease(cfdata); ??? */
1733 qunlock(&clip.lk);
1734 }
1735 #endif /* APPLESNARF */
1736
1737 void
1738 rpc_putsnarf(char *data)
1739 {
1740 #ifdef APPLESNARF
1741 _appleputsnarf(data);
1742 #endif
1743 __xputsnarf(data);
1744 }
1745
1746 /*
1747 * Send the mouse event back to the window manager.
1748 * So that 9term can tell rio to pop up its button3 menu.
1749 */
1750 void
1751 rpc_bouncemouse(Client *c, Mouse m)
1752 {
1753 Xwin *w = (Xwin*)c->view;
1754 XButtonEvent e;
1755 XWindow dw;
1756
1757 xlock();
1758 e.type = ButtonPress;
1759 e.state = 0;
1760 e.button = 0;
1761 if(m.buttons&1)
1762 e.button = 1;
1763 else if(m.buttons&2)
1764 e.button = 2;
1765 else if(m.buttons&4)
1766 e.button = 3;
1767 e.same_screen = 1;
1768 XTranslateCoordinates(_x.display, w->drawable,
1769 DefaultRootWindow(_x.display),
1770 m.xy.x, m.xy.y, &e.x_root, &e.y_root, &dw);
1771 e.root = DefaultRootWindow(_x.display);
1772 e.window = e.root;
1773 e.subwindow = None;
1774 e.x = e.x_root;
1775 e.y = e.y_root;
1776 #undef time
1777 e.time = CurrentTime;
1778 XUngrabPointer(_x.display, m.msec);
1779 XSendEvent(_x.display, e.root, True, ButtonPressMask, (XEvent*)&e);
1780 XFlush(_x.display);
1781 xunlock();
1782 }