blob: f9940eefb9330bd2102164c7c7df6eaf3589f4c4 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2003-2005 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package sun.awt.X11;
27
28import java.awt.dnd.DnDConstants;
29
30import java.nio.ByteOrder;
31
32import java.util.Arrays;
33
34import sun.misc.Unsafe;
35
36/**
37 * Motif DnD protocol global constants and convenience routines.
38 *
39 * @since 1.5
40 */
41class MotifDnDConstants {
42 // Note that offsets in all native structures below do not depend on the
43 // architecture.
44 private static final Unsafe unsafe = XlibWrapper.unsafe;
45 static final XAtom XA_MOTIF_ATOM_0 = XAtom.get("_MOTIF_ATOM_0");
46 static final XAtom XA_MOTIF_DRAG_WINDOW = XAtom.get("_MOTIF_DRAG_WINDOW");
47 static final XAtom XA_MOTIF_DRAG_TARGETS = XAtom.get("_MOTIF_DRAG_TARGETS");
48 static final XAtom XA_MOTIF_DRAG_INITIATOR_INFO =
49 XAtom.get("_MOTIF_DRAG_INITIATOR_INFO");
50 static final XAtom XA_MOTIF_DRAG_RECEIVER_INFO =
51 XAtom.get("_MOTIF_DRAG_RECEIVER_INFO");
52 static final XAtom XA_MOTIF_DRAG_AND_DROP_MESSAGE =
53 XAtom.get("_MOTIF_DRAG_AND_DROP_MESSAGE");
54 static final XAtom XA_XmTRANSFER_SUCCESS =
55 XAtom.get("XmTRANSFER_SUCCESS");
56 static final XAtom XA_XmTRANSFER_FAILURE =
57 XAtom.get("XmTRANSFER_FAILURE");
58 static final XSelection MotifDnDSelection =
59 new XSelection(XA_MOTIF_ATOM_0, null);
60
61 public static final byte MOTIF_DND_PROTOCOL_VERSION = 0;
62
63 /* Supported protocol styles */
64 public static final int MOTIF_PREFER_PREREGISTER_STYLE = 2;
65 public static final int MOTIF_PREFER_DYNAMIC_STYLE = 4;
66 public static final int MOTIF_DYNAMIC_STYLE = 5;
67 public static final int MOTIF_PREFER_RECEIVER_STYLE = 6;
68
69 /* Info structure sizes */
70 public static final int MOTIF_INITIATOR_INFO_SIZE = 8;
71 public static final int MOTIF_RECEIVER_INFO_SIZE = 16;
72
73 /* Sender/reason message masks */
74 public static final byte MOTIF_MESSAGE_REASON_MASK = (byte)0x7F;
75 public static final byte MOTIF_MESSAGE_SENDER_MASK = (byte)0x80;
76 public static final byte MOTIF_MESSAGE_FROM_RECEIVER = (byte)0x80;
77 public static final byte MOTIF_MESSAGE_FROM_INITIATOR = (byte)0;
78
79 /* Message flags masks and shifts */
80 public static final int MOTIF_DND_ACTION_MASK = 0x000F;
81 public static final int MOTIF_DND_ACTION_SHIFT = 0;
82 public static final int MOTIF_DND_STATUS_MASK = 0x00F0;
83 public static final int MOTIF_DND_STATUS_SHIFT = 4;
84 public static final int MOTIF_DND_ACTIONS_MASK = 0x0F00;
85 public static final int MOTIF_DND_ACTIONS_SHIFT = 8;
86
87 /* message type constants */
88 public static final byte TOP_LEVEL_ENTER = 0;
89 public static final byte TOP_LEVEL_LEAVE = 1;
90 public static final byte DRAG_MOTION = 2;
91 public static final byte DROP_SITE_ENTER = 3;
92 public static final byte DROP_SITE_LEAVE = 4;
93 public static final byte DROP_START = 5;
94 public static final byte DROP_FINISH = 6;
95 public static final byte DRAG_DROP_FINISH = 7;
96 public static final byte OPERATION_CHANGED = 8;
97
98 /* drop action constants */
99 public static final int MOTIF_DND_NOOP = 0;
100 public static final int MOTIF_DND_MOVE = 1 << 0;
101 public static final int MOTIF_DND_COPY = 1 << 1;
102 public static final int MOTIF_DND_LINK = 1 << 2;
103
104 /* drop site status constants */
105 public static final byte MOTIF_NO_DROP_SITE = (byte)1;
106 public static final byte MOTIF_INVALID_DROP_SITE = (byte)2;
107 public static final byte MOTIF_VALID_DROP_SITE = (byte)3;
108
109 private static long readMotifWindow() throws XException {
110 long defaultScreenNumber = XlibWrapper.DefaultScreen(XToolkit.getDisplay());
111 long defaultRootWindow =
112 XlibWrapper.RootWindow(XToolkit.getDisplay(), defaultScreenNumber);
113
114 long motifWindow = 0;
115
116 WindowPropertyGetter wpg = new WindowPropertyGetter(defaultRootWindow,
117 XA_MOTIF_DRAG_WINDOW,
118 0, 1,
119 false,
120 XlibWrapper.AnyPropertyType);
121 try {
122 int status = wpg.execute(XToolkit.IgnoreBadWindowHandler);
123
124 if (status == XlibWrapper.Success &&
125 wpg.getData() != 0 &&
126 wpg.getActualType() == XAtom.XA_WINDOW &&
127 wpg.getActualFormat() == 32 &&
128 wpg.getNumberOfItems() == 1) {
129 long data = wpg.getData();
130 // XID is CARD32.
131 motifWindow = Native.getLong(data);
132 }
133
134 return motifWindow;
135 } finally {
136 wpg.dispose();
137 }
138 }
139
140 private static long createMotifWindow() throws XException {
141 assert XToolkit.isAWTLockHeldByCurrentThread();
142
143 long defaultScreenNumber =
144 XlibWrapper.DefaultScreen(XToolkit.getDisplay());
145 long defaultRootWindow =
146 XlibWrapper.RootWindow(XToolkit.getDisplay(), defaultScreenNumber);
147
148 long motifWindow = 0;
149
150 long displayString = XlibWrapper.XDisplayString(XToolkit.getDisplay());
151
152 if (displayString == 0) {
153 throw new XException("XDisplayString returns NULL");
154 }
155
156 long newDisplay = XlibWrapper.XOpenDisplay(displayString);
157
158 if (newDisplay == 0) {
159 throw new XException("XOpenDisplay returns NULL");
160 }
161
162 XlibWrapper.XGrabServer(newDisplay);
163
164 try {
165 XlibWrapper.XSetCloseDownMode(newDisplay, (int)XlibWrapper.RetainPermanent);
166
167 XSetWindowAttributes xwa = new XSetWindowAttributes();
168
169 try {
170 xwa.set_override_redirect(true);
171 xwa.set_event_mask(XlibWrapper.PropertyChangeMask);
172
173 motifWindow = XlibWrapper.XCreateWindow(newDisplay, defaultRootWindow,
174 -10, -10, 1, 1, 0, 0,
175 XlibWrapper.InputOnly,
176 XlibWrapper.CopyFromParent,
177 (XlibWrapper.CWOverrideRedirect |
178 XlibWrapper.CWEventMask),
179 xwa.pData);
180
181 if (motifWindow == 0) {
182 throw new XException("XCreateWindow returns NULL");
183 }
184
185 XlibWrapper.XMapWindow(newDisplay, motifWindow);
186
187 long data = Native.allocateLongArray(1);
188
189 try {
190 Native.putLong(data, motifWindow);
191
192 XToolkit.WITH_XERROR_HANDLER(XWM.VerifyChangePropertyHandler);
193 XlibWrapper.XChangeProperty(XToolkit.getDisplay(),
194 defaultRootWindow,
195 XA_MOTIF_DRAG_WINDOW.getAtom(),
196 XAtom.XA_WINDOW, 32,
197 XlibWrapper.PropModeReplace,
198 data, 1);
199
200 XToolkit.RESTORE_XERROR_HANDLER();
201
202 if (XToolkit.saved_error != null &&
203 XToolkit.saved_error.get_error_code() != XlibWrapper.Success) {
204 throw new XException("Cannot write motif drag window handle.");
205 }
206
207 return motifWindow;
208 } finally {
209 unsafe.freeMemory(data);
210 }
211 } finally {
212 xwa.dispose();
213 }
214 } finally {
215 XlibWrapper.XUngrabServer(newDisplay);
216 XlibWrapper.XCloseDisplay(newDisplay);
217 }
218 }
219
220 private static long getMotifWindow() throws XException {
221 /*
222 * Note: it is unsafe to cache the motif drag window handle, as another
223 * client can change the _MOTIF_DRAG_WINDOW property on the root, the handle
224 * becomes out-of-sync and all subsequent drag operations will fail.
225 */
226 long motifWindow = readMotifWindow();
227 if (motifWindow == 0) {
228 motifWindow = createMotifWindow();
229 }
230 return motifWindow;
231 }
232
233 public static final class Swapper {
234 public static short swap(short s) {
235 return (short)(((s & 0xFF00) >>> 8) | ((s & 0xFF) << 8));
236 }
237 public static int swap(int i) {
238 return ((i & 0xFF000000) >>> 24) | ((i & 0x00FF0000) >>> 8) |
239 ((i & 0x0000FF00) << 8) | ((i & 0x000000FF) << 24);
240 }
241
242 public static short getShort(long data, byte order) {
243 short s = unsafe.getShort(data);
244 if (order != MotifDnDConstants.getByteOrderByte()) {
245 return swap(s);
246 } else {
247 return s;
248 }
249 }
250 public static int getInt(long data, byte order) {
251 int i = unsafe.getInt(data);
252 if (order != MotifDnDConstants.getByteOrderByte()) {
253 return swap(i);
254 } else {
255 return i;
256 }
257 }
258 }
259
260 /**
261 * DragBSI.h:
262 *
263 * typedef struct {
264 * BYTE byte_order;
265 * BYTE protocol_version;
266 * CARD16 num_target_lists B16;
267 * CARD32 heap_offset B32;
268 * } xmMotifTargetsPropertyRec;
269 */
270 private static long[][] getTargetListTable(long motifWindow)
271 throws XException {
272
273 WindowPropertyGetter wpg = new WindowPropertyGetter(motifWindow,
274 XA_MOTIF_DRAG_TARGETS,
275 0, 100000L,
276 false,
277 XA_MOTIF_DRAG_TARGETS.getAtom());
278 try {
279 int status = wpg.execute(XToolkit.IgnoreBadWindowHandler);
280
281 if (status != XlibWrapper.Success
282 || wpg.getActualType() != XA_MOTIF_DRAG_TARGETS.getAtom()
283 || wpg.getData() == 0) {
284
285 return null;
286 }
287
288 long data = wpg.getData();
289
290 if (unsafe.getByte(data + 1) != MOTIF_DND_PROTOCOL_VERSION) {
291 return null;
292 }
293
294 boolean swapNeeded = unsafe.getByte(data + 0) != getByteOrderByte();
295
296 short numTargetLists = unsafe.getShort(data + 2);
297
298 if (swapNeeded) {
299 numTargetLists = Swapper.swap(numTargetLists);
300 }
301
302 long[][] table = new long[numTargetLists][];
303 ByteOrder byteOrder = ByteOrder.nativeOrder();
304 if (swapNeeded) {
305 byteOrder = (byteOrder == ByteOrder.LITTLE_ENDIAN) ?
306 ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN;
307 }
308
309 long bufptr = data + 8;
310 for (short i = 0; i < numTargetLists; i++) {
311 short numTargets = unsafe.getShort(bufptr);
312 bufptr += 2;
313 if (swapNeeded) {
314 numTargets = Swapper.swap(numTargets);
315 }
316
317 table[i] = new long[numTargets];
318
319 for (short j = 0; j < numTargets; j++) {
320 // NOTE: cannot use Unsafe.getInt(), since it crashes on
321 // Solaris/Sparc if the address is not a multiple of 4.
322 int target = 0;
323 if (byteOrder == ByteOrder.LITTLE_ENDIAN) {
324 for (int idx = 0; idx < 4; idx++) {
325 target |= (unsafe.getByte(bufptr + idx) << 8*idx)
326 & (0xFF << 8*idx);
327 }
328 } else {
329 for (int idx = 0; idx < 4; idx++) {
330 target |= (unsafe.getByte(bufptr + idx) << 8*(3-idx))
331 & (0xFF << 8*(3-idx));
332 }
333 }
334 // NOTE: don't need to swap, since we read it in the proper
335 // order already.
336 table[i][j] = target;
337 bufptr += 4;
338 }
339 }
340 return table;
341 } finally {
342 wpg.dispose();
343 }
344 }
345
346 private static void putTargetListTable(long motifWindow, long[][] table)
347 throws XException {
348 assert XToolkit.isAWTLockHeldByCurrentThread();
349
350 int tableSize = 8; /* The size of leading xmMotifTargetsPropertyRec. */
351
352 for (int i = 0; i < table.length; i++) {
353 tableSize += table[i].length * 4 + 2;
354 }
355
356 long data = unsafe.allocateMemory(tableSize);
357
358 try {
359 // BYTE byte_order;
360 unsafe.putByte(data + 0, getByteOrderByte());
361 // BYTE protocol_version;
362 unsafe.putByte(data + 1, MOTIF_DND_PROTOCOL_VERSION);
363 // CARD16 num_target_lists B16;
364 unsafe.putShort(data + 2, (short)table.length);
365 // CARD32 heap_offset B32;
366 unsafe.putInt(data + 4, tableSize);
367
368 long bufptr = data + 8;
369
370 for (int i = 0; i < table.length; i++) {
371 unsafe.putShort(bufptr, (short)table[i].length);
372 bufptr += 2;
373
374 for (int j = 0; j < table[i].length; j++) {
375 int target = (int)table[i][j];
376 // NOTE: cannot use Unsafe.putInt(), since it crashes on
377 // Solaris/Sparc if the address is not a multiple of 4.
378 if (ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN) {
379 for (int idx = 0; idx < 4; idx++) {
380 byte b = (byte)((target & (0xFF << (8*idx))) >> (8*idx));
381 unsafe.putByte(bufptr + idx, b);
382 }
383 } else {
384 for (int idx = 0; idx < 4; idx++) {
385 byte b = (byte)((target & (0xFF << (8*idx))) >> (8*idx));
386 unsafe.putByte(bufptr + (3-idx), b);
387 }
388 }
389 bufptr += 4;
390 }
391 }
392
393 XToolkit.WITH_XERROR_HANDLER(XWM.VerifyChangePropertyHandler);
394 XlibWrapper.XChangeProperty(XToolkit.getDisplay(),
395 motifWindow,
396 XA_MOTIF_DRAG_TARGETS.getAtom(),
397 XA_MOTIF_DRAG_TARGETS.getAtom(), 8,
398 XlibWrapper.PropModeReplace,
399 data, tableSize);
400
401 XToolkit.RESTORE_XERROR_HANDLER();
402
403 if (XToolkit.saved_error != null &&
404 XToolkit.saved_error.get_error_code() != XlibWrapper.Success) {
405
406 // Create a new motif window and retry.
407 motifWindow = createMotifWindow();
408
409 XToolkit.WITH_XERROR_HANDLER(XWM.VerifyChangePropertyHandler);
410 XlibWrapper.XChangeProperty(XToolkit.getDisplay(),
411 motifWindow,
412 XA_MOTIF_DRAG_TARGETS.getAtom(),
413 XA_MOTIF_DRAG_TARGETS.getAtom(), 8,
414 XlibWrapper.PropModeReplace,
415 data, tableSize);
416
417 XToolkit.RESTORE_XERROR_HANDLER();
418
419 if (XToolkit.saved_error != null &&
420 XToolkit.saved_error.get_error_code() != XlibWrapper.Success) {
421 throw new XException("Cannot write motif drag targets property.");
422 }
423 }
424 } finally {
425 unsafe.freeMemory(data);
426 }
427 }
428
429 static int getIndexForTargetList(long[] formats) throws XException {
430 assert XToolkit.isAWTLockHeldByCurrentThread();
431
432 if (formats.length > 0) {
433 // Make a defensive copy.
434 formats = (long[])formats.clone();
435
436 Arrays.sort(formats);
437 }
438
439 // NOTE: getMotifWindow() should never be called if the server is
440 // grabbed. This will lock up the application as it grabs the server
441 // itself.
442 // Since we don't grab the server before getMotifWindow(), another
443 // client might replace motif window after we read it from the root, but
444 // before we grab the server.
445 // We cannot resolve this problem, but we believe that this scenario is
446 // very unlikely to happen.
447 long motifWindow = getMotifWindow();
448
449 XlibWrapper.XGrabServer(XToolkit.getDisplay());
450
451 try {
452 long[][] table = getTargetListTable(motifWindow);
453
454 if (table != null) {
455 for (int i = 0; i < table.length; i++) {
456 boolean equals = true;
457 if (table[i].length == formats.length) {
458 for (int j = 0; j < table[i].length; j++) {
459 if (table[i][j] != formats[j]) {
460 equals = false;
461 break;
462 }
463 }
464 } else {
465 equals = false;
466 }
467
468 if (equals) {
469 XlibWrapper.XUngrabServer(XToolkit.getDisplay());
470 return i;
471 }
472 }
473 } else {
474 // Create a new table.
475 // The first two entries must always be the same.
476 // (see DragBS.c)
477 table = new long[2][];
478 table[0] = new long[] { 0 };
479 table[1] = new long[] { XAtom.XA_STRING };
480 }
481
482 /* Index not found - expand the targets table. */
483 long[][] new_table = new long[table.length + 1][];
484
485 /* Copy the old contents to the new table. */
486 for (int i = 0; i < table.length; i++) {
487 new_table[i] = table[i];
488 }
489
490 /* Fill in the new entry */
491 new_table[new_table.length - 1] = formats;
492
493 putTargetListTable(motifWindow, new_table);
494
495 return new_table.length - 1;
496 } finally {
497 XlibWrapper.XUngrabServer(XToolkit.getDisplay());
498 }
499 }
500
501 static long[] getTargetListForIndex(int index) {
502 long motifWindow = getMotifWindow();
503 long[][] table = getTargetListTable(motifWindow);
504
505 if (index < 0 || index >= table.length) {
506 return new long[0];
507 } else {
508 return table[index];
509 }
510 }
511
512 static byte getByteOrderByte() {
513 // 'l' - for little endian, 'B' - for big endian.
514 return ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN ?
515 (byte)0x6C : (byte)0x42;
516 }
517
518 static void writeDragInitiatorInfoStruct(long window, int index) throws XException {
519 assert XToolkit.isAWTLockHeldByCurrentThread();
520
521 long structData = unsafe.allocateMemory(MOTIF_INITIATOR_INFO_SIZE);
522
523 try {
524 // BYTE byte_order
525 unsafe.putByte(structData, getByteOrderByte());
526 // BYTE protocol_version
527 unsafe.putByte(structData + 1, MOTIF_DND_PROTOCOL_VERSION);
528 // CARD16 protocol_version
529 unsafe.putShort(structData + 2, (short)index);
530 // CARD32 icc_handle
531 unsafe.putInt(structData + 4, (int)XA_MOTIF_ATOM_0.getAtom());
532
533 XToolkit.WITH_XERROR_HANDLER(XWM.VerifyChangePropertyHandler);
534 XlibWrapper.XChangeProperty(XToolkit.getDisplay(), window,
535 XA_MOTIF_ATOM_0.getAtom(),
536 XA_MOTIF_DRAG_INITIATOR_INFO.getAtom(),
537 8, XlibWrapper.PropModeReplace,
538 structData, MOTIF_INITIATOR_INFO_SIZE);
539 XToolkit.RESTORE_XERROR_HANDLER();
540
541 if (XToolkit.saved_error != null &&
542 XToolkit.saved_error.get_error_code() != XlibWrapper.Success) {
543 throw new XException("Cannot write drag initiator info");
544 }
545 } finally {
546 unsafe.freeMemory(structData);
547 }
548 }
549
550 static void writeDragReceiverInfoStruct(long window) throws XException {
551 assert XToolkit.isAWTLockHeldByCurrentThread();
552
553 int dataSize = MotifDnDConstants.MOTIF_RECEIVER_INFO_SIZE;
554 long data = unsafe.allocateMemory(dataSize);
555
556 try {
557 unsafe.putByte(data, MotifDnDConstants.getByteOrderByte()); /* byte order */
558 unsafe.putByte(data + 1, MotifDnDConstants.MOTIF_DND_PROTOCOL_VERSION); /* protocol version */
559 unsafe.putByte(data + 2, (byte)MotifDnDConstants.MOTIF_DYNAMIC_STYLE); /* protocol style */
560 unsafe.putByte(data + 3, (byte)0); /* pad */
561 unsafe.putInt(data + 4, (int)window); /* proxy window */
562 unsafe.putShort(data + 8, (short)0); /* num_drop_sites */
563 unsafe.putShort(data + 10, (short)0); /* pad */
564 unsafe.putInt(data + 12, dataSize);
565
566 XToolkit.WITH_XERROR_HANDLER(XWM.VerifyChangePropertyHandler);
567 XlibWrapper.XChangeProperty(XToolkit.getDisplay(), window,
568 XA_MOTIF_DRAG_RECEIVER_INFO.getAtom(),
569 XA_MOTIF_DRAG_RECEIVER_INFO.getAtom(),
570 8, XlibWrapper.PropModeReplace,
571 data, dataSize);
572 XToolkit.RESTORE_XERROR_HANDLER();
573
574 if (XToolkit.saved_error != null &&
575 XToolkit.saved_error.get_error_code() != XlibWrapper.Success) {
576 throw new XException("Cannot write Motif receiver info property");
577 }
578 } finally {
579 unsafe.freeMemory(data);
580 }
581 }
582
583 public static int getMotifActionsForJavaActions(int javaActions) {
584 int motifActions = MOTIF_DND_NOOP;
585
586 if ((javaActions & DnDConstants.ACTION_MOVE) != 0) {
587 motifActions |= MOTIF_DND_MOVE;
588 }
589 if ((javaActions & DnDConstants.ACTION_COPY) != 0) {
590 motifActions |= MOTIF_DND_COPY;
591 }
592 if ((javaActions & DnDConstants.ACTION_LINK) != 0) {
593 motifActions |= MOTIF_DND_LINK;
594 }
595
596 return motifActions;
597 }
598
599 public static int getJavaActionsForMotifActions(int motifActions) {
600 int javaActions = DnDConstants.ACTION_NONE;
601
602 if ((motifActions & MOTIF_DND_MOVE) != 0) {
603 javaActions |= DnDConstants.ACTION_MOVE;
604 }
605 if ((motifActions & MOTIF_DND_COPY) != 0) {
606 javaActions |= DnDConstants.ACTION_COPY;
607 }
608 if ((motifActions & MOTIF_DND_LINK) != 0) {
609 javaActions |= DnDConstants.ACTION_LINK;
610 }
611
612 return javaActions;
613 }
614}