rsc | 76193d7 | 2003-09-30 17:47:42 +0000 | [diff] [blame] | 1 | /* input event and data structure translation */ |
| 2 | |
rsc | 76193d7 | 2003-09-30 17:47:42 +0000 | [diff] [blame] | 3 | #include <u.h> |
rsc | be22ae2 | 2004-03-26 01:59:35 +0000 | [diff] [blame] | 4 | #include "x11-inc.h" |
rsc | a240659 | 2006-02-28 12:54:15 +0000 | [diff] [blame] | 5 | #ifdef __APPLE__ |
| 6 | #define APPLESNARF |
| 7 | #define Boolean AppleBoolean |
| 8 | #define Rect AppleRect |
| 9 | #define EventMask AppleEventMask |
| 10 | #define Point ApplePoint |
| 11 | #define Cursor AppleCursor |
| 12 | #include <Carbon/Carbon.h> |
| 13 | AUTOFRAMEWORK(Carbon) |
| 14 | #undef Boolean |
| 15 | #undef Rect |
| 16 | #undef EventMask |
| 17 | #undef Point |
| 18 | #undef Cursor |
| 19 | #endif |
rsc | 76193d7 | 2003-09-30 17:47:42 +0000 | [diff] [blame] | 20 | #include <libc.h> |
| 21 | #include <draw.h> |
| 22 | #include <memdraw.h> |
| 23 | #include <mouse.h> |
| 24 | #include <cursor.h> |
| 25 | #include <keyboard.h> |
| 26 | #include "x11-memdraw.h" |
rsc | e543c47 | 2004-04-19 05:56:17 +0000 | [diff] [blame] | 27 | #include "x11-keysym2ucs.h" |
rsc | 8ad5179 | 2004-03-25 23:03:57 +0000 | [diff] [blame] | 28 | #undef time |
| 29 | |
rsc | e543c47 | 2004-04-19 05:56:17 +0000 | [diff] [blame] | 30 | static KeySym |
rsc | 32f69c3 | 2003-12-11 17:48:38 +0000 | [diff] [blame] | 31 | __xtoplan9kbd(XEvent *e) |
rsc | 76193d7 | 2003-09-30 17:47:42 +0000 | [diff] [blame] | 32 | { |
rsc | e543c47 | 2004-04-19 05:56:17 +0000 | [diff] [blame] | 33 | KeySym k; |
rsc | 76193d7 | 2003-09-30 17:47:42 +0000 | [diff] [blame] | 34 | |
rsc | e543c47 | 2004-04-19 05:56:17 +0000 | [diff] [blame] | 35 | if(e->xany.type != KeyPress) |
| 36 | return -1; |
rsc | a0e8d02 | 2005-01-23 16:02:04 +0000 | [diff] [blame] | 37 | needstack(64*1024); /* X has some *huge* buffers in openobject */ |
| 38 | /* and they're even bigger on SuSE */ |
rsc | e543c47 | 2004-04-19 05:56:17 +0000 | [diff] [blame] | 39 | XLookupString((XKeyEvent*)e,NULL,0,&k,NULL); |
rsc | 76193d7 | 2003-09-30 17:47:42 +0000 | [diff] [blame] | 40 | if(k == XK_Multi_key || k == NoSymbol) |
| 41 | return -1; |
| 42 | |
| 43 | if(k&0xFF00){ |
| 44 | switch(k){ |
| 45 | case XK_BackSpace: |
| 46 | case XK_Tab: |
| 47 | case XK_Escape: |
| 48 | case XK_Delete: |
| 49 | case XK_KP_0: |
| 50 | case XK_KP_1: |
| 51 | case XK_KP_2: |
| 52 | case XK_KP_3: |
| 53 | case XK_KP_4: |
| 54 | case XK_KP_5: |
| 55 | case XK_KP_6: |
| 56 | case XK_KP_7: |
| 57 | case XK_KP_8: |
| 58 | case XK_KP_9: |
| 59 | case XK_KP_Divide: |
| 60 | case XK_KP_Multiply: |
| 61 | case XK_KP_Subtract: |
| 62 | case XK_KP_Add: |
| 63 | case XK_KP_Decimal: |
| 64 | k &= 0x7F; |
| 65 | break; |
| 66 | case XK_Linefeed: |
| 67 | k = '\r'; |
| 68 | break; |
| 69 | case XK_KP_Space: |
| 70 | k = ' '; |
| 71 | break; |
| 72 | case XK_Home: |
| 73 | case XK_KP_Home: |
| 74 | k = Khome; |
| 75 | break; |
| 76 | case XK_Left: |
| 77 | case XK_KP_Left: |
| 78 | k = Kleft; |
| 79 | break; |
| 80 | case XK_Up: |
| 81 | case XK_KP_Up: |
| 82 | k = Kup; |
| 83 | break; |
| 84 | case XK_Down: |
| 85 | case XK_KP_Down: |
| 86 | k = Kdown; |
| 87 | break; |
| 88 | case XK_Right: |
| 89 | case XK_KP_Right: |
| 90 | k = Kright; |
| 91 | break; |
| 92 | case XK_Page_Down: |
| 93 | case XK_KP_Page_Down: |
| 94 | k = Kpgdown; |
| 95 | break; |
| 96 | case XK_End: |
| 97 | case XK_KP_End: |
| 98 | k = Kend; |
| 99 | break; |
| 100 | case XK_Page_Up: |
| 101 | case XK_KP_Page_Up: |
| 102 | k = Kpgup; |
| 103 | break; |
| 104 | case XK_Insert: |
| 105 | case XK_KP_Insert: |
| 106 | k = Kins; |
| 107 | break; |
| 108 | case XK_KP_Enter: |
| 109 | case XK_Return: |
| 110 | k = '\n'; |
| 111 | break; |
| 112 | case XK_Alt_L: |
rsc | 1aa4e9c | 2004-04-23 05:17:54 +0000 | [diff] [blame] | 113 | case XK_Meta_L: /* Shift Alt on PCs */ |
rsc | 76193d7 | 2003-09-30 17:47:42 +0000 | [diff] [blame] | 114 | case XK_Alt_R: |
rsc | 1aa4e9c | 2004-04-23 05:17:54 +0000 | [diff] [blame] | 115 | case XK_Meta_R: /* Shift Alt on PCs */ |
rsc | 76193d7 | 2003-09-30 17:47:42 +0000 | [diff] [blame] | 116 | k = Kalt; |
| 117 | break; |
| 118 | default: /* not ISO-1 or tty control */ |
rsc | e543c47 | 2004-04-19 05:56:17 +0000 | [diff] [blame] | 119 | if(k>0xff) { |
rsc | 1818ce0 | 2005-01-04 22:20:14 +0000 | [diff] [blame] | 120 | k = _p9keysym2ucs(k); |
rsc | e543c47 | 2004-04-19 05:56:17 +0000 | [diff] [blame] | 121 | if(k==-1) return -1; |
| 122 | } |
rsc | 76193d7 | 2003-09-30 17:47:42 +0000 | [diff] [blame] | 123 | } |
| 124 | } |
| 125 | |
| 126 | /* Compensate for servers that call a minus a hyphen */ |
| 127 | if(k == XK_hyphen) |
| 128 | k = XK_minus; |
| 129 | /* Do control mapping ourselves if translator doesn't */ |
| 130 | if(e->xkey.state&ControlMask) |
| 131 | k &= 0x9f; |
| 132 | if(k == NoSymbol) { |
| 133 | return -1; |
| 134 | } |
| 135 | |
rsc | e543c47 | 2004-04-19 05:56:17 +0000 | [diff] [blame] | 136 | return k+0; |
rsc | 76193d7 | 2003-09-30 17:47:42 +0000 | [diff] [blame] | 137 | } |
| 138 | |
rsc | 16a7096 | 2003-11-23 18:15:43 +0000 | [diff] [blame] | 139 | static Rune* |
| 140 | xtoplan9latin1(XEvent *e) |
| 141 | { |
| 142 | static Rune k[10]; |
| 143 | static int alting, nk; |
| 144 | int n; |
| 145 | int r; |
| 146 | |
rsc | 32f69c3 | 2003-12-11 17:48:38 +0000 | [diff] [blame] | 147 | r = __xtoplan9kbd(e); |
rsc | 16a7096 | 2003-11-23 18:15:43 +0000 | [diff] [blame] | 148 | if(r < 0) |
| 149 | return nil; |
| 150 | if(alting){ |
rsc | e66de6b | 2004-06-17 03:31:47 +0000 | [diff] [blame] | 151 | /* |
| 152 | * Kludge for Mac's X11 3-button emulation. |
| 153 | * It treats Command+Button as button 3, but also |
| 154 | * ends up sending XK_Meta_L twice. |
| 155 | */ |
| 156 | if(r == Kalt){ |
| 157 | alting = 0; |
| 158 | return nil; |
| 159 | } |
rsc | 16a7096 | 2003-11-23 18:15:43 +0000 | [diff] [blame] | 160 | k[nk++] = r; |
| 161 | n = _latin1(k, nk); |
| 162 | if(n > 0){ |
| 163 | alting = 0; |
| 164 | k[0] = n; |
| 165 | k[1] = 0; |
| 166 | return k; |
| 167 | } |
| 168 | if(n == -1){ |
| 169 | alting = 0; |
| 170 | k[nk] = 0; |
| 171 | return k; |
| 172 | } |
| 173 | /* n < -1, need more input */ |
| 174 | return nil; |
| 175 | }else if(r == Kalt){ |
| 176 | alting = 1; |
| 177 | nk = 0; |
| 178 | return nil; |
| 179 | }else{ |
| 180 | k[0] = r; |
| 181 | k[1] = 0; |
| 182 | return k; |
| 183 | } |
| 184 | } |
| 185 | |
rsc | 76193d7 | 2003-09-30 17:47:42 +0000 | [diff] [blame] | 186 | int |
rsc | 32f69c3 | 2003-12-11 17:48:38 +0000 | [diff] [blame] | 187 | _xtoplan9kbd(XEvent *e) |
rsc | 16a7096 | 2003-11-23 18:15:43 +0000 | [diff] [blame] | 188 | { |
| 189 | static Rune *r; |
| 190 | |
| 191 | if(e == (XEvent*)-1){ |
| 192 | assert(r); |
| 193 | r--; |
| 194 | return 0; |
| 195 | } |
| 196 | if(e) |
| 197 | r = xtoplan9latin1(e); |
| 198 | if(r && *r) |
| 199 | return *r++; |
| 200 | return -1; |
| 201 | } |
| 202 | |
| 203 | int |
rsc | 32f69c3 | 2003-12-11 17:48:38 +0000 | [diff] [blame] | 204 | _xtoplan9mouse(XDisplay *xd, XEvent *e, Mouse *m) |
rsc | 76193d7 | 2003-09-30 17:47:42 +0000 | [diff] [blame] | 205 | { |
| 206 | int s; |
| 207 | XButtonEvent *be; |
| 208 | XMotionEvent *me; |
| 209 | |
rsc | 16a7096 | 2003-11-23 18:15:43 +0000 | [diff] [blame] | 210 | if(_x.putsnarf != _x.assertsnarf){ |
| 211 | _x.assertsnarf = _x.putsnarf; |
| 212 | XSetSelectionOwner(_x.mousecon, XA_PRIMARY, _x.drawable, CurrentTime); |
| 213 | if(_x.clipboard != None) |
| 214 | XSetSelectionOwner(_x.mousecon, _x.clipboard, _x.drawable, CurrentTime); |
| 215 | XFlush(xd); |
| 216 | } |
| 217 | |
rsc | 76193d7 | 2003-09-30 17:47:42 +0000 | [diff] [blame] | 218 | switch(e->type){ |
| 219 | case ButtonPress: |
| 220 | be = (XButtonEvent*)e; |
rsc | f701258 | 2003-11-25 01:40:27 +0000 | [diff] [blame] | 221 | /* |
| 222 | * Fake message, just sent to make us announce snarf. |
| 223 | * Apparently state and button are 16 and 8 bits on |
| 224 | * the wire, since they are truncated by the time they |
| 225 | * get to us. |
| 226 | */ |
| 227 | if(be->send_event |
| 228 | && (~be->state&0xFFFF)==0 |
| 229 | && (~be->button&0xFF)==0) |
rsc | 16a7096 | 2003-11-23 18:15:43 +0000 | [diff] [blame] | 230 | return -1; |
rsc | 76193d7 | 2003-09-30 17:47:42 +0000 | [diff] [blame] | 231 | /* BUG? on mac need to inherit these from elsewhere? */ |
| 232 | m->xy.x = be->x; |
| 233 | m->xy.y = be->y; |
| 234 | s = be->state; |
| 235 | m->msec = be->time; |
| 236 | switch(be->button){ |
| 237 | case 1: |
| 238 | s |= Button1Mask; |
| 239 | break; |
| 240 | case 2: |
| 241 | s |= Button2Mask; |
| 242 | break; |
| 243 | case 3: |
| 244 | s |= Button3Mask; |
| 245 | break; |
rsc | ff8bbc7 | 2004-06-09 14:01:30 +0000 | [diff] [blame] | 246 | case 4: |
| 247 | s |= Button4Mask; |
| 248 | break; |
| 249 | case 5: |
| 250 | s |= Button5Mask; |
| 251 | break; |
rsc | 76193d7 | 2003-09-30 17:47:42 +0000 | [diff] [blame] | 252 | } |
| 253 | break; |
| 254 | case ButtonRelease: |
| 255 | be = (XButtonEvent*)e; |
| 256 | m->xy.x = be->x; |
| 257 | m->xy.y = be->y; |
| 258 | s = be->state; |
| 259 | m->msec = be->time; |
| 260 | switch(be->button){ |
| 261 | case 1: |
| 262 | s &= ~Button1Mask; |
| 263 | break; |
| 264 | case 2: |
| 265 | s &= ~Button2Mask; |
| 266 | break; |
| 267 | case 3: |
| 268 | s &= ~Button3Mask; |
| 269 | break; |
rsc | ff8bbc7 | 2004-06-09 14:01:30 +0000 | [diff] [blame] | 270 | case 4: |
| 271 | s &= ~Button4Mask; |
| 272 | break; |
| 273 | case 5: |
| 274 | s &= ~Button5Mask; |
| 275 | break; |
rsc | 76193d7 | 2003-09-30 17:47:42 +0000 | [diff] [blame] | 276 | } |
| 277 | break; |
| 278 | |
| 279 | case MotionNotify: |
| 280 | me = (XMotionEvent*)e; |
| 281 | s = me->state; |
| 282 | m->xy.x = me->x; |
| 283 | m->xy.y = me->y; |
| 284 | m->msec = me->time; |
| 285 | break; |
| 286 | |
| 287 | default: |
| 288 | return -1; |
| 289 | } |
| 290 | |
| 291 | m->buttons = 0; |
| 292 | if(s & Button1Mask) |
| 293 | m->buttons |= 1; |
| 294 | if(s & Button2Mask) |
| 295 | m->buttons |= 2; |
| 296 | if(s & Button3Mask) |
| 297 | m->buttons |= 4; |
rsc | ff8bbc7 | 2004-06-09 14:01:30 +0000 | [diff] [blame] | 298 | if(s & Button4Mask) |
| 299 | m->buttons |= 8; |
| 300 | if(s & Button5Mask) |
| 301 | m->buttons |= 16; |
rsc | 76193d7 | 2003-09-30 17:47:42 +0000 | [diff] [blame] | 302 | return 0; |
| 303 | } |
| 304 | |
| 305 | void |
rsc | 32f69c3 | 2003-12-11 17:48:38 +0000 | [diff] [blame] | 306 | _xmoveto(Point p) |
rsc | 76193d7 | 2003-09-30 17:47:42 +0000 | [diff] [blame] | 307 | { |
| 308 | XWarpPointer(_x.display, None, _x.drawable, 0, 0, 0, 0, p.x, p.y); |
| 309 | XFlush(_x.display); |
| 310 | } |
| 311 | |
| 312 | static int |
| 313 | revbyte(int b) |
| 314 | { |
| 315 | int r; |
| 316 | |
| 317 | r = 0; |
| 318 | r |= (b&0x01) << 7; |
| 319 | r |= (b&0x02) << 5; |
| 320 | r |= (b&0x04) << 3; |
| 321 | r |= (b&0x08) << 1; |
| 322 | r |= (b&0x10) >> 1; |
| 323 | r |= (b&0x20) >> 3; |
| 324 | r |= (b&0x40) >> 5; |
| 325 | r |= (b&0x80) >> 7; |
| 326 | return r; |
| 327 | } |
| 328 | |
| 329 | static void |
| 330 | xcursorarrow(void) |
| 331 | { |
| 332 | if(_x.cursor != 0){ |
| 333 | XFreeCursor(_x.display, _x.cursor); |
| 334 | _x.cursor = 0; |
| 335 | } |
| 336 | XUndefineCursor(_x.display, _x.drawable); |
| 337 | XFlush(_x.display); |
| 338 | } |
| 339 | |
| 340 | |
| 341 | void |
rsc | 32f69c3 | 2003-12-11 17:48:38 +0000 | [diff] [blame] | 342 | _xsetcursor(Cursor *c) |
rsc | 76193d7 | 2003-09-30 17:47:42 +0000 | [diff] [blame] | 343 | { |
| 344 | XColor fg, bg; |
| 345 | XCursor xc; |
| 346 | Pixmap xsrc, xmask; |
| 347 | int i; |
| 348 | uchar src[2*16], mask[2*16]; |
| 349 | |
| 350 | if(c == nil){ |
| 351 | xcursorarrow(); |
| 352 | return; |
| 353 | } |
| 354 | for(i=0; i<2*16; i++){ |
| 355 | src[i] = revbyte(c->set[i]); |
| 356 | mask[i] = revbyte(c->set[i] | c->clr[i]); |
| 357 | } |
| 358 | |
| 359 | fg = _x.map[0]; |
| 360 | bg = _x.map[255]; |
rsc | be22ae2 | 2004-03-26 01:59:35 +0000 | [diff] [blame] | 361 | xsrc = XCreateBitmapFromData(_x.display, _x.drawable, (char*)src, 16, 16); |
| 362 | xmask = XCreateBitmapFromData(_x.display, _x.drawable, (char*)mask, 16, 16); |
rsc | 76193d7 | 2003-09-30 17:47:42 +0000 | [diff] [blame] | 363 | xc = XCreatePixmapCursor(_x.display, xsrc, xmask, &fg, &bg, -c->offset.x, -c->offset.y); |
| 364 | if(xc != 0) { |
| 365 | XDefineCursor(_x.display, _x.drawable, xc); |
| 366 | if(_x.cursor != 0) |
| 367 | XFreeCursor(_x.display, _x.cursor); |
| 368 | _x.cursor = xc; |
| 369 | } |
| 370 | XFreePixmap(_x.display, xsrc); |
| 371 | XFreePixmap(_x.display, xmask); |
| 372 | XFlush(_x.display); |
| 373 | } |
| 374 | |
rsc | 161060a | 2003-10-11 02:47:43 +0000 | [diff] [blame] | 375 | struct { |
rsc | 161060a | 2003-10-11 02:47:43 +0000 | [diff] [blame] | 376 | QLock lk; |
rsc | a240659 | 2006-02-28 12:54:15 +0000 | [diff] [blame] | 377 | char buf[SnarfSize]; |
| 378 | #ifdef APPLESNARF |
| 379 | Rune rbuf[SnarfSize]; |
| 380 | PasteboardRef apple; |
| 381 | #endif |
rsc | 161060a | 2003-10-11 02:47:43 +0000 | [diff] [blame] | 382 | } clip; |
| 383 | |
| 384 | char* |
rsc | 32f69c3 | 2003-12-11 17:48:38 +0000 | [diff] [blame] | 385 | _xgetsnarf(XDisplay *xd) |
rsc | 161060a | 2003-10-11 02:47:43 +0000 | [diff] [blame] | 386 | { |
| 387 | uchar *data, *xdata; |
rsc | 16a7096 | 2003-11-23 18:15:43 +0000 | [diff] [blame] | 388 | Atom clipboard, type, prop; |
rsc | 161060a | 2003-10-11 02:47:43 +0000 | [diff] [blame] | 389 | ulong len, lastlen, dummy; |
| 390 | int fmt, i; |
| 391 | XWindow w; |
| 392 | |
| 393 | qlock(&clip.lk); |
rsc | 16a7096 | 2003-11-23 18:15:43 +0000 | [diff] [blame] | 394 | /* |
rsc | 7e9e092 | 2005-05-02 04:20:14 +0000 | [diff] [blame] | 395 | * Have we snarfed recently and the X server hasn't caught up? |
| 396 | */ |
| 397 | if(_x.putsnarf != _x.assertsnarf) |
| 398 | goto mine; |
| 399 | |
| 400 | /* |
rsc | 16a7096 | 2003-11-23 18:15:43 +0000 | [diff] [blame] | 401 | * Is there a primary selection (highlighted text in an xterm)? |
| 402 | */ |
| 403 | clipboard = XA_PRIMARY; |
rsc | 161060a | 2003-10-11 02:47:43 +0000 | [diff] [blame] | 404 | w = XGetSelectionOwner(xd, XA_PRIMARY); |
| 405 | if(w == _x.drawable){ |
rsc | 16a7096 | 2003-11-23 18:15:43 +0000 | [diff] [blame] | 406 | mine: |
rsc | 161060a | 2003-10-11 02:47:43 +0000 | [diff] [blame] | 407 | data = (uchar*)strdup(clip.buf); |
| 408 | goto out; |
| 409 | } |
rsc | 16a7096 | 2003-11-23 18:15:43 +0000 | [diff] [blame] | 410 | |
| 411 | /* |
| 412 | * If not, is there a clipboard selection? |
| 413 | */ |
| 414 | if(w == None && _x.clipboard != None){ |
| 415 | clipboard = _x.clipboard; |
| 416 | w = XGetSelectionOwner(xd, _x.clipboard); |
| 417 | if(w == _x.drawable) |
| 418 | goto mine; |
| 419 | } |
| 420 | |
| 421 | /* |
| 422 | * If not, give up. |
| 423 | */ |
rsc | 161060a | 2003-10-11 02:47:43 +0000 | [diff] [blame] | 424 | if(w == None){ |
| 425 | data = nil; |
| 426 | goto out; |
| 427 | } |
rsc | 16a7096 | 2003-11-23 18:15:43 +0000 | [diff] [blame] | 428 | |
rsc | 161060a | 2003-10-11 02:47:43 +0000 | [diff] [blame] | 429 | /* |
| 430 | * We should be waiting for SelectionNotify here, but it might never |
rsc | 16a7096 | 2003-11-23 18:15:43 +0000 | [diff] [blame] | 431 | * come, and we have no way to time out. Instead, we will clear |
| 432 | * local property #1, request our buddy to fill it in for us, and poll |
| 433 | * until he's done or we get tired of waiting. |
rsc | e39b8b1 | 2003-12-02 03:42:46 +0000 | [diff] [blame] | 434 | * |
| 435 | * We should try to go for _x.utf8string instead of XA_STRING, |
| 436 | * but that would add to the polling. |
rsc | 161060a | 2003-10-11 02:47:43 +0000 | [diff] [blame] | 437 | */ |
rsc | 16a7096 | 2003-11-23 18:15:43 +0000 | [diff] [blame] | 438 | prop = 1; |
| 439 | XChangeProperty(xd, _x.drawable, prop, XA_STRING, 8, PropModeReplace, (uchar*)"", 0); |
| 440 | XConvertSelection(xd, clipboard, XA_STRING, prop, _x.drawable, CurrentTime); |
rsc | 161060a | 2003-10-11 02:47:43 +0000 | [diff] [blame] | 441 | XFlush(xd); |
| 442 | lastlen = 0; |
rsc | 16a7096 | 2003-11-23 18:15:43 +0000 | [diff] [blame] | 443 | for(i=0; i<10 || (lastlen!=0 && i<30); i++){ |
rsc | 161060a | 2003-10-11 02:47:43 +0000 | [diff] [blame] | 444 | usleep(100*1000); |
rsc | 16a7096 | 2003-11-23 18:15:43 +0000 | [diff] [blame] | 445 | XGetWindowProperty(xd, _x.drawable, prop, 0, 0, 0, AnyPropertyType, |
rsc | 161060a | 2003-10-11 02:47:43 +0000 | [diff] [blame] | 446 | &type, &fmt, &dummy, &len, &data); |
| 447 | if(lastlen == len && len > 0) |
| 448 | break; |
| 449 | lastlen = len; |
| 450 | } |
rsc | e39b8b1 | 2003-12-02 03:42:46 +0000 | [diff] [blame] | 451 | if(i == 10){ |
rsc | 161060a | 2003-10-11 02:47:43 +0000 | [diff] [blame] | 452 | data = nil; |
| 453 | goto out; |
| 454 | } |
| 455 | /* get the property */ |
| 456 | data = nil; |
rsc | 16a7096 | 2003-11-23 18:15:43 +0000 | [diff] [blame] | 457 | XGetWindowProperty(xd, _x.drawable, prop, 0, SnarfSize/sizeof(ulong), 0, |
rsc | 161060a | 2003-10-11 02:47:43 +0000 | [diff] [blame] | 458 | AnyPropertyType, &type, &fmt, &len, &dummy, &xdata); |
rsc | e39b8b1 | 2003-12-02 03:42:46 +0000 | [diff] [blame] | 459 | if((type != XA_STRING && type != _x.utf8string) || len == 0){ |
rsc | 161060a | 2003-10-11 02:47:43 +0000 | [diff] [blame] | 460 | if(xdata) |
| 461 | XFree(xdata); |
| 462 | data = nil; |
| 463 | }else{ |
| 464 | if(xdata){ |
rsc | be22ae2 | 2004-03-26 01:59:35 +0000 | [diff] [blame] | 465 | data = (uchar*)strdup((char*)xdata); |
rsc | 161060a | 2003-10-11 02:47:43 +0000 | [diff] [blame] | 466 | XFree(xdata); |
| 467 | }else |
| 468 | data = nil; |
| 469 | } |
| 470 | out: |
| 471 | qunlock(&clip.lk); |
rsc | be22ae2 | 2004-03-26 01:59:35 +0000 | [diff] [blame] | 472 | return (char*)data; |
rsc | 161060a | 2003-10-11 02:47:43 +0000 | [diff] [blame] | 473 | } |
| 474 | |
| 475 | void |
rsc | 32f69c3 | 2003-12-11 17:48:38 +0000 | [diff] [blame] | 476 | _xputsnarf(XDisplay *xd, char *data) |
rsc | 161060a | 2003-10-11 02:47:43 +0000 | [diff] [blame] | 477 | { |
rsc | 16a7096 | 2003-11-23 18:15:43 +0000 | [diff] [blame] | 478 | XButtonEvent e; |
| 479 | |
rsc | 161060a | 2003-10-11 02:47:43 +0000 | [diff] [blame] | 480 | if(strlen(data) >= SnarfSize) |
| 481 | return; |
| 482 | qlock(&clip.lk); |
| 483 | strcpy(clip.buf, data); |
rsc | 16a7096 | 2003-11-23 18:15:43 +0000 | [diff] [blame] | 484 | /* leave note for mouse proc to assert selection ownership */ |
| 485 | _x.putsnarf++; |
| 486 | |
| 487 | /* send mouse a fake event so snarf is announced */ |
| 488 | memset(&e, 0, sizeof e); |
| 489 | e.type = ButtonPress; |
| 490 | e.window = _x.drawable; |
| 491 | e.state = ~0; |
| 492 | e.button = ~0; |
| 493 | XSendEvent(xd, _x.drawable, True, ButtonPressMask, (XEvent*)&e); |
rsc | 161060a | 2003-10-11 02:47:43 +0000 | [diff] [blame] | 494 | XFlush(xd); |
| 495 | qunlock(&clip.lk); |
| 496 | } |
| 497 | |
| 498 | int |
rsc | 32f69c3 | 2003-12-11 17:48:38 +0000 | [diff] [blame] | 499 | _xselect(XEvent *e, XDisplay *xd) |
rsc | 161060a | 2003-10-11 02:47:43 +0000 | [diff] [blame] | 500 | { |
rsc | e39b8b1 | 2003-12-02 03:42:46 +0000 | [diff] [blame] | 501 | char *name; |
rsc | 161060a | 2003-10-11 02:47:43 +0000 | [diff] [blame] | 502 | XEvent r; |
| 503 | XSelectionRequestEvent *xe; |
rsc | e39b8b1 | 2003-12-02 03:42:46 +0000 | [diff] [blame] | 504 | Atom a[4]; |
rsc | 161060a | 2003-10-11 02:47:43 +0000 | [diff] [blame] | 505 | |
| 506 | memset(&r, 0, sizeof r); |
| 507 | xe = (XSelectionRequestEvent*)e; |
rsc | 4cdbf87 | 2004-03-02 23:16:37 +0000 | [diff] [blame] | 508 | if(0) fprint(2, "xselect target=%d requestor=%d property=%d selection=%d\n", |
rsc | e39b8b1 | 2003-12-02 03:42:46 +0000 | [diff] [blame] | 509 | xe->target, xe->requestor, xe->property, xe->selection); |
| 510 | r.xselection.property = xe->property; |
| 511 | if(xe->target == _x.targets){ |
| 512 | a[0] = XA_STRING; |
| 513 | a[1] = _x.utf8string; |
| 514 | a[2] = _x.text; |
| 515 | a[3] = _x.compoundtext; |
| 516 | |
| 517 | XChangeProperty(xd, xe->requestor, xe->property, xe->target, |
| 518 | 8, PropModeReplace, (uchar*)a, sizeof a); |
rsc | b9b6535 | 2006-03-10 00:37:15 +0000 | [diff] [blame] | 519 | }else if(xe->target == XA_STRING |
| 520 | || xe->target == _x.utf8string |
| 521 | || xe->target == _x.text |
| 522 | || xe->target == _x.compoundtext |
| 523 | || ((name = XGetAtomName(xd, xe->target)) && strcmp(name, "text/plain;charset=UTF-8") == 0)){ |
| 524 | /* text/plain;charset=UTF-8 seems nonstandard but is used by Synergy */ |
rsc | e39b8b1 | 2003-12-02 03:42:46 +0000 | [diff] [blame] | 525 | /* if the target is STRING we're supposed to reply with Latin1 XXX */ |
rsc | 161060a | 2003-10-11 02:47:43 +0000 | [diff] [blame] | 526 | qlock(&clip.lk); |
rsc | e39b8b1 | 2003-12-02 03:42:46 +0000 | [diff] [blame] | 527 | XChangeProperty(xd, xe->requestor, xe->property, xe->target, |
| 528 | 8, PropModeReplace, (uchar*)clip.buf, strlen(clip.buf)); |
rsc | 161060a | 2003-10-11 02:47:43 +0000 | [diff] [blame] | 529 | qunlock(&clip.lk); |
rsc | 161060a | 2003-10-11 02:47:43 +0000 | [diff] [blame] | 530 | }else{ |
rsc | 49fda44 | 2004-04-22 15:19:59 +0000 | [diff] [blame] | 531 | if(strcmp(name, "TIMESTAMP") != 0) |
| 532 | fprint(2, "%s: cannot handle selection request for '%s' (%d)\n", argv0, name, (int)xe->target); |
rsc | 161060a | 2003-10-11 02:47:43 +0000 | [diff] [blame] | 533 | r.xselection.property = None; |
| 534 | } |
| 535 | |
| 536 | r.xselection.display = xe->display; |
| 537 | /* r.xselection.property filled above */ |
| 538 | r.xselection.target = xe->target; |
| 539 | r.xselection.type = SelectionNotify; |
| 540 | r.xselection.requestor = xe->requestor; |
| 541 | r.xselection.time = xe->time; |
| 542 | r.xselection.send_event = True; |
| 543 | r.xselection.selection = xe->selection; |
| 544 | XSendEvent(xd, xe->requestor, False, 0, &r); |
| 545 | XFlush(xd); |
| 546 | return 0; |
| 547 | } |
| 548 | |
rsc | a240659 | 2006-02-28 12:54:15 +0000 | [diff] [blame] | 549 | #ifdef APPLESNARF |
| 550 | char* |
| 551 | applegetsnarf(void) |
| 552 | { |
| 553 | char *s, *t; |
| 554 | CFArrayRef flavors; |
| 555 | CFDataRef data; |
| 556 | CFIndex nflavor, ndata, j; |
| 557 | CFStringRef type; |
| 558 | ItemCount nitem; |
| 559 | PasteboardItemID id; |
| 560 | PasteboardSyncFlags flags; |
| 561 | UInt32 i; |
rsc | 4fc1aa0 | 2006-02-28 13:13:39 +0000 | [diff] [blame] | 562 | |
| 563 | // fprint(2, "applegetsnarf\n"); |
rsc | a240659 | 2006-02-28 12:54:15 +0000 | [diff] [blame] | 564 | qlock(&clip.lk); |
| 565 | if(clip.apple == nil){ |
| 566 | if(PasteboardCreate(kPasteboardClipboard, &clip.apple) != noErr){ |
| 567 | fprint(2, "apple pasteboard create failed\n"); |
| 568 | qunlock(&clip.lk); |
| 569 | return nil; |
| 570 | } |
| 571 | } |
| 572 | flags = PasteboardSynchronize(clip.apple); |
| 573 | if(flags&kPasteboardClientIsOwner){ |
rsc | 4fc1aa0 | 2006-02-28 13:13:39 +0000 | [diff] [blame] | 574 | s = strdup(clip.buf); |
rsc | a240659 | 2006-02-28 12:54:15 +0000 | [diff] [blame] | 575 | qunlock(&clip.lk); |
rsc | 4fc1aa0 | 2006-02-28 13:13:39 +0000 | [diff] [blame] | 576 | return s; |
rsc | a240659 | 2006-02-28 12:54:15 +0000 | [diff] [blame] | 577 | } |
| 578 | if(PasteboardGetItemCount(clip.apple, &nitem) != noErr){ |
| 579 | fprint(2, "apple pasteboard get item count failed\n"); |
| 580 | qunlock(&clip.lk); |
| 581 | return nil; |
| 582 | } |
| 583 | for(i=1; i<=nitem; i++){ |
| 584 | if(PasteboardGetItemIdentifier(clip.apple, i, &id) != noErr) |
| 585 | continue; |
| 586 | if(PasteboardCopyItemFlavors(clip.apple, id, &flavors) != noErr) |
| 587 | continue; |
| 588 | nflavor = CFArrayGetCount(flavors); |
| 589 | for(j=0; j<nflavor; j++){ |
| 590 | type = (CFStringRef)CFArrayGetValueAtIndex(flavors, j); |
| 591 | if(!UTTypeConformsTo(type, CFSTR("public.utf16-plain-text"))) |
| 592 | continue; |
| 593 | if(PasteboardCopyItemFlavorData(clip.apple, id, type, &data) != noErr) |
| 594 | continue; |
| 595 | ndata = CFDataGetLength(data); |
| 596 | qunlock(&clip.lk); |
| 597 | s = smprint("%.*S", ndata/2, (Rune*)CFDataGetBytePtr(data)); |
| 598 | CFRelease(flavors); |
| 599 | CFRelease(data); |
| 600 | for(t=s; *t; t++) |
| 601 | if(*t == '\r') |
| 602 | *t = '\n'; |
| 603 | return s; |
| 604 | } |
| 605 | CFRelease(flavors); |
| 606 | } |
| 607 | qunlock(&clip.lk); |
| 608 | return nil; |
| 609 | } |
| 610 | |
| 611 | void |
| 612 | appleputsnarf(char *s) |
| 613 | { |
| 614 | CFDataRef cfdata; |
| 615 | PasteboardSyncFlags flags; |
| 616 | |
rsc | 4fc1aa0 | 2006-02-28 13:13:39 +0000 | [diff] [blame] | 617 | // fprint(2, "appleputsnarf\n"); |
| 618 | |
rsc | a240659 | 2006-02-28 12:54:15 +0000 | [diff] [blame] | 619 | if(strlen(s) >= SnarfSize) |
| 620 | return; |
| 621 | qlock(&clip.lk); |
| 622 | strcpy(clip.buf, s); |
| 623 | runesnprint(clip.rbuf, nelem(clip.rbuf), "%s", s); |
| 624 | if(clip.apple == nil){ |
| 625 | if(PasteboardCreate(kPasteboardClipboard, &clip.apple) != noErr){ |
| 626 | fprint(2, "apple pasteboard create failed\n"); |
| 627 | qunlock(&clip.lk); |
| 628 | return; |
| 629 | } |
| 630 | } |
| 631 | if(PasteboardClear(clip.apple) != noErr){ |
| 632 | fprint(2, "apple pasteboard clear failed\n"); |
| 633 | qunlock(&clip.lk); |
| 634 | return; |
| 635 | } |
| 636 | flags = PasteboardSynchronize(clip.apple); |
| 637 | if((flags&kPasteboardModified) || !(flags&kPasteboardClientIsOwner)){ |
| 638 | fprint(2, "apple pasteboard cannot assert ownership\n"); |
| 639 | qunlock(&clip.lk); |
| 640 | return; |
| 641 | } |
| 642 | cfdata = CFDataCreate(kCFAllocatorDefault, |
| 643 | (uchar*)clip.rbuf, runestrlen(clip.rbuf)*2); |
| 644 | if(cfdata == nil){ |
| 645 | fprint(2, "apple pasteboard cfdatacreate failed\n"); |
| 646 | qunlock(&clip.lk); |
| 647 | return; |
| 648 | } |
| 649 | if(PasteboardPutItemFlavor(clip.apple, (PasteboardItemID)1, |
| 650 | CFSTR("public.utf16-plain-text"), cfdata, 0) != noErr){ |
| 651 | fprint(2, "apple pasteboard putitem failed\n"); |
| 652 | CFRelease(cfdata); |
| 653 | qunlock(&clip.lk); |
| 654 | return; |
| 655 | } |
| 656 | /* CFRelease(cfdata); ??? */ |
| 657 | qunlock(&clip.lk); |
| 658 | } |
rsc | 4fc1aa0 | 2006-02-28 13:13:39 +0000 | [diff] [blame] | 659 | static int useapplesnarf = -1; |
| 660 | static int |
| 661 | checkapplesnarf(void) |
| 662 | { |
| 663 | char *x; |
| 664 | |
| 665 | x = getenv("USEX11SNARF"); |
| 666 | if(x && x[0]) |
| 667 | return 0; |
| 668 | return 1; |
| 669 | } |
rsc | a240659 | 2006-02-28 12:54:15 +0000 | [diff] [blame] | 670 | #endif /* APPLESNARF */ |
| 671 | |
rsc | 161060a | 2003-10-11 02:47:43 +0000 | [diff] [blame] | 672 | void |
| 673 | putsnarf(char *data) |
| 674 | { |
rsc | a240659 | 2006-02-28 12:54:15 +0000 | [diff] [blame] | 675 | #ifdef APPLESNARF |
rsc | 23fdde4 | 2006-02-28 19:48:49 +0000 | [diff] [blame] | 676 | appleputsnarf(data); |
rsc | a240659 | 2006-02-28 12:54:15 +0000 | [diff] [blame] | 677 | #endif |
rsc | 32f69c3 | 2003-12-11 17:48:38 +0000 | [diff] [blame] | 678 | _xputsnarf(_x.snarfcon, data); |
rsc | 161060a | 2003-10-11 02:47:43 +0000 | [diff] [blame] | 679 | } |
| 680 | |
| 681 | char* |
| 682 | getsnarf(void) |
| 683 | { |
rsc | 32f69c3 | 2003-12-11 17:48:38 +0000 | [diff] [blame] | 684 | return _xgetsnarf(_x.snarfcon); |
rsc | 161060a | 2003-10-11 02:47:43 +0000 | [diff] [blame] | 685 | } |
| 686 | |