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