/*
 * Copyright (c) 2003, 2006, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

#ifdef HEADLESS
    #error This file should not be included in headless library
#endif

#include "awt_dnd.h"

#include "awt_p.h"

#include "java_awt_dnd_DnDConstants.h"

/* Shared atoms */

Atom XA_WM_STATE;
Atom XA_DELETE;

/* XDnD atoms */

Atom XA_XdndAware;
Atom XA_XdndProxy;

Atom XA_XdndEnter;
Atom XA_XdndPosition;
Atom XA_XdndLeave;
Atom XA_XdndDrop;
Atom XA_XdndStatus;
Atom XA_XdndFinished;

Atom XA_XdndTypeList;
Atom XA_XdndSelection;

Atom XA_XdndActionCopy;
Atom XA_XdndActionMove;
Atom XA_XdndActionLink;
Atom XA_XdndActionAsk;
Atom XA_XdndActionPrivate;
Atom XA_XdndActionList;

/* Motif DnD atoms */

Atom _XA_MOTIF_DRAG_WINDOW;
Atom _XA_MOTIF_DRAG_TARGETS;
Atom _XA_MOTIF_DRAG_INITIATOR_INFO;
Atom _XA_MOTIF_DRAG_RECEIVER_INFO;
Atom _XA_MOTIF_DRAG_AND_DROP_MESSAGE;
Atom _XA_MOTIF_ATOM_0;
Atom XA_XmTRANSFER_SUCCESS;
Atom XA_XmTRANSFER_FAILURE;

unsigned char MOTIF_BYTE_ORDER = 0;

static Window awt_root_window = None;

static Boolean
init_atoms(Display* display) {
    struct atominit {
        Atom *atomptr;
        const char *name;
    };

    /* Add new atoms to this list */
    static struct atominit atom_list[] = {
        /* Shared atoms */
        { &XA_WM_STATE,                     "WM_STATE"                     },
        { &XA_DELETE,                       "DELETE"                       },

        /* XDnD atoms */
        { &XA_XdndAware,                    "XdndAware"                    },
        { &XA_XdndProxy,                    "XdndProxy"                    },
        { &XA_XdndEnter,                    "XdndEnter"                    },
        { &XA_XdndPosition,                 "XdndPosition"                 },
        { &XA_XdndLeave,                    "XdndLeave"                    },
        { &XA_XdndDrop,                     "XdndDrop"                     },
        { &XA_XdndStatus,                   "XdndStatus"                   },
        { &XA_XdndFinished,                 "XdndFinished"                 },
        { &XA_XdndTypeList,                 "XdndTypeList"                 },
        { &XA_XdndSelection,                "XdndSelection"                },
        { &XA_XdndActionCopy,               "XdndActionCopy"               },
        { &XA_XdndActionMove,               "XdndActionMove"               },
        { &XA_XdndActionLink,               "XdndActionLink"               },
        { &XA_XdndActionAsk,                "XdndActionAsk"                },
        { &XA_XdndActionPrivate,            "XdndActionPrivate"            },
        { &XA_XdndActionList,               "XdndActionList"               },

        /* Motif DnD atoms */
        { &_XA_MOTIF_DRAG_WINDOW,           "_MOTIF_DRAG_WINDOW"           },
        { &_XA_MOTIF_DRAG_TARGETS,          "_MOTIF_DRAG_TARGETS"          },
        { &_XA_MOTIF_DRAG_INITIATOR_INFO,   "_MOTIF_DRAG_INITIATOR_INFO"   },
        { &_XA_MOTIF_DRAG_RECEIVER_INFO,    "_MOTIF_DRAG_RECEIVER_INFO"    },
        { &_XA_MOTIF_DRAG_AND_DROP_MESSAGE, "_MOTIF_DRAG_AND_DROP_MESSAGE" },
        { &_XA_MOTIF_ATOM_0,                "_MOTIF_ATOM_0"                },
        { &XA_XmTRANSFER_SUCCESS,           "XmTRANSFER_SUCCESS"           },
        { &XA_XmTRANSFER_FAILURE,           "XmTRANSFER_FAILURE"           }
    };

#define ATOM_LIST_LENGTH (sizeof(atom_list)/sizeof(atom_list[0]))

    const char *names[ATOM_LIST_LENGTH];
    Atom atoms[ATOM_LIST_LENGTH];
    Status status;
    size_t i;

    /* Fill the array of atom names */
    for (i = 0; i < ATOM_LIST_LENGTH; ++i) {
        names[i] = atom_list[i].name;
    }

    DTRACE_PRINT2("%s:%d initializing atoms ... ", __FILE__, __LINE__);

    status = XInternAtoms(awt_display, (char**)names, ATOM_LIST_LENGTH,
                          False, atoms);
    if (status == 0) {
        DTRACE_PRINTLN("failed");
        return False;
    }

    /* Store returned atoms into corresponding global variables */
    DTRACE_PRINTLN("ok");
    for (i = 0; i < ATOM_LIST_LENGTH; ++i) {
        *atom_list[i].atomptr = atoms[i];
    }

    return True;
#undef ATOM_LIST_LENGTH
}

/*
 * NOTE: must be called after awt_root_shell is created and realized.
 */
Boolean
awt_dnd_init(Display* display) {
    static Boolean inited = False;

    if (!inited) {
        Boolean atoms_inited = False;
        Boolean ds_inited = False;
        unsigned int value = 1;
        MOTIF_BYTE_ORDER = (*((char*)&value) != 0) ? 'l' : 'B';

        /* NOTE: init_atoms() should be called before the rest of initialization
           so that atoms can be used. */
        inited = init_atoms(display);

        if (inited) {
            if (XtIsRealized(awt_root_shell)) {
                awt_root_window = XtWindow(awt_root_shell);
            } else {
                inited = False;
            }
        }

        inited = inited && awt_dnd_ds_init(display);
    }

    return inited;
}

/*
 * Returns a window of awt_root_shell.
 */
Window
get_awt_root_window() {
    return awt_root_window;
}

static unsigned char local_xerror_code = Success;

static int
xerror_handler(Display *dpy, XErrorEvent *err) {
    local_xerror_code = err->error_code;
    return 0;
}

/**************** checked_X* wrappers *****************************************/
#undef NO_SYNC
#undef SYNC_TRACE

unsigned char
checked_XChangeProperty(Display* display, Window w, Atom property, Atom type,
                        int format, int mode, unsigned char* data,
                        int nelements) {
    XErrorHandler xerror_saved_handler;

#ifndef NO_SYNC
    XSync(display, False);
#ifdef SYNC_TRACE
    fprintf(stderr,"XSync 1\n");
#endif
#endif
    local_xerror_code = Success;
    xerror_saved_handler = XSetErrorHandler(xerror_handler);

    XChangeProperty(display, w, property, type, format, mode, data, nelements);

#ifndef NO_SYNC
    XSync(display, False);
#ifdef SYNC_TRACE
    fprintf(stderr,"XSync 2\n");
#endif
#endif
    XSetErrorHandler(xerror_saved_handler);

    return local_xerror_code;
}

unsigned char
checked_XGetWindowProperty(Display* display, Window w, Atom property, long long_offset,
                           long long_length, Bool delete, Atom req_type,
                           Atom* actual_type_return, int* actual_format_return,
                           unsigned long* nitems_return, unsigned long* bytes_after_return,
                           unsigned char** prop_return) {

    XErrorHandler xerror_saved_handler;
    int ret_val = Success;

#ifndef NO_SYNC
    XSync(display, False);
#ifdef SYNC_TRACE
    fprintf(stderr,"XSync 3\n");
#endif
#endif
    local_xerror_code = Success;
    xerror_saved_handler = XSetErrorHandler(xerror_handler);

    ret_val = XGetWindowProperty(display, w, property, long_offset, long_length,
                                 delete, req_type, actual_type_return,
                                 actual_format_return, nitems_return,
                                 bytes_after_return, prop_return);

#ifndef NO_SYNC
    XSync(display, False);
#ifdef SYNC_TRACE
    fprintf(stderr,"XSync 4\n");
#endif
#endif
    XSetErrorHandler(xerror_saved_handler);

    return ret_val != Success ? local_xerror_code : Success;
}

unsigned char
checked_XSendEvent(Display* display, Window w, Bool propagate, long event_mask,
                   XEvent* event_send) {

    XErrorHandler xerror_saved_handler;
    Status ret_val = 0;

#ifndef NO_SYNC
    XSync(display, False);
#ifdef SYNC_TRACE
    fprintf(stderr,"XSync 5\n");
#endif
#endif
    local_xerror_code = Success;
    xerror_saved_handler = XSetErrorHandler(xerror_handler);

    ret_val = XSendEvent(display, w, propagate, event_mask, event_send);

#ifndef NO_SYNC
    XSync(display, False);
#ifdef SYNC_TRACE
    fprintf(stderr,"XSync 6\n");
#endif
#endif
    XSetErrorHandler(xerror_saved_handler);

    return ret_val == 0 ? local_xerror_code : Success;
}

/*
 * NOTE: returns Success even if the two windows aren't on the same screen.
 */
unsigned char
checked_XTranslateCoordinates(Display* display, Window src_w, Window dest_w,
                              int src_x, int src_y, int* dest_x_return,
                              int* dest_y_return, Window* child_return) {

    XErrorHandler xerror_saved_handler;
    Bool ret_val = True;

#ifndef NO_SYNC
    XSync(display, False);
#ifdef SYNC_TRACE
    fprintf(stderr,"XSync 7\n");
#endif
#endif
    local_xerror_code = Success;
    xerror_saved_handler = XSetErrorHandler(xerror_handler);

    ret_val = XTranslateCoordinates(display, src_w, dest_w, src_x, src_y,
                                    dest_x_return, dest_y_return, child_return);

#ifndef NO_SYNC
    XSync(display, False);
#ifdef SYNC_TRACE
    fprintf(stderr,"XSync 8\n");
#endif
#endif
    XSetErrorHandler(xerror_saved_handler);

    return local_xerror_code;
}

unsigned char
checked_XSelectInput(Display* display, Window w, long event_mask) {
    XErrorHandler xerror_saved_handler;
    Bool ret_val = True;

#ifndef NO_SYNC
    XSync(display, False);
#ifdef SYNC_TRACE
    fprintf(stderr,"XSync 7\n");
#endif
#endif
    local_xerror_code = Success;
    xerror_saved_handler = XSetErrorHandler(xerror_handler);

    XSelectInput(display, w, event_mask);

#ifndef NO_SYNC
    XSync(display, False);
#ifdef SYNC_TRACE
    fprintf(stderr,"XSync 8\n");
#endif
#endif
    XSetErrorHandler(xerror_saved_handler);

    return local_xerror_code;
}
/******************************************************************************/

jint
xdnd_to_java_action(Atom action) {
    if (action == XA_XdndActionCopy) {
        return java_awt_dnd_DnDConstants_ACTION_COPY;
    } else if (action == XA_XdndActionMove) {
        return java_awt_dnd_DnDConstants_ACTION_MOVE;
    } else if (action == XA_XdndActionLink) {
        return java_awt_dnd_DnDConstants_ACTION_LINK;
    } else if (action == None) {
        return java_awt_dnd_DnDConstants_ACTION_NONE;
    } else {
        /* XdndActionCopy is the default. */
        return java_awt_dnd_DnDConstants_ACTION_COPY;
    }
}

Atom
java_to_xdnd_action(jint action) {
    switch (action) {
    case java_awt_dnd_DnDConstants_ACTION_COPY: return XA_XdndActionCopy;
    case java_awt_dnd_DnDConstants_ACTION_MOVE: return XA_XdndActionMove;
    case java_awt_dnd_DnDConstants_ACTION_LINK: return XA_XdndActionLink;
    default:                                    return None;
    }
}

void
write_card8(void** p, CARD8 value) {
    CARD8** card8_pp = (CARD8**)p;
    **card8_pp = value;
    (*card8_pp)++;
}

void
write_card16(void** p, CARD16 value) {
    CARD16** card16_pp = (CARD16**)p;
    **card16_pp = value;
    (*card16_pp)++;
}

void
write_card32(void** p, CARD32 value) {
    CARD32** card32_pp = (CARD32**)p;
    **card32_pp = value;
    (*card32_pp)++;
}

CARD8
read_card8(char* data, size_t offset) {
    return *((CARD8*)(data + offset));
}

CARD16
read_card16(char* data, size_t offset, char byte_order) {
    CARD16 card16 = *((CARD16*)(data + offset));

    if (byte_order != MOTIF_BYTE_ORDER) {
        SWAP2BYTES(card16);
    }

    return card16;
}

CARD32
read_card32(char* data, size_t offset, char byte_order) {
    CARD32 card32 = *((CARD32*)(data + offset));

    if (byte_order != MOTIF_BYTE_ORDER) {
        SWAP4BYTES(card32);
    }

    return card32;
}

static Window
read_motif_window(Display* dpy) {
    Window         root_window = DefaultRootWindow(dpy);
    Window         motif_window = None;

    unsigned char  ret;
    Atom           type;
    int            format;
    unsigned long  nitems;
    unsigned long  after;
    unsigned char  *data;

    ret = checked_XGetWindowProperty(dpy, root_window, _XA_MOTIF_DRAG_WINDOW,
                                     0, 0xFFFF, False, AnyPropertyType, &type,
                                     &format, &nitems, &after, &data);

    if (ret != Success) {
        DTRACE_PRINTLN2("%s:%d Failed to read _MOTIF_DRAG_WINDOW.",
                        __FILE__, __LINE__);
        return None;
    }


    if (type == XA_WINDOW && format == 32 && nitems == 1) {
        motif_window = *((Window*)data);
    }

    XFree ((char *)data);

    return motif_window;
}

static Window
create_motif_window(Display* dpy) {
    Window   root_window = DefaultRootWindow(dpy);
    Window   motif_window = None;
    Display* display = NULL;
    XSetWindowAttributes swa;

    display = XOpenDisplay(XDisplayString(dpy));
    if (display == NULL) {
        return None;
    }

    XGrabServer(display);

    XSetCloseDownMode(display, RetainPermanent);

    swa.override_redirect = True;
    swa.event_mask = PropertyChangeMask;
    motif_window = XCreateWindow(display, root_window,
                                 -10, -10, 1, 1, 0, 0,
                                 InputOnly, CopyFromParent,
                                 (CWOverrideRedirect|CWEventMask),
                                 &swa);
    XMapWindow(display, motif_window);

    XChangeProperty(display, root_window, _XA_MOTIF_DRAG_WINDOW, XA_WINDOW, 32,
                    PropModeReplace, (unsigned char *)&motif_window, 1);

    XUngrabServer(display);

    XCloseDisplay(display);

    return motif_window;
}

Window
get_motif_window(Display* dpy) {
    /*
     * Note: it is unsafe to cache the motif drag window handle, as another
     * client can change the _MOTIF_DRAG_WINDOW property on the root, the handle
     * becomes out-of-sync and all subsequent drag operations will fail.
     */
    Window motif_window = read_motif_window(dpy);
    if (motif_window == None) {
        motif_window = create_motif_window(dpy);
    }

    return motif_window;
}

typedef struct {
    CARD16 num_targets;
    Atom* targets;
} TargetsTableEntry;

typedef struct {
    CARD16 num_entries;
    TargetsTableEntry* entries;
} TargetsTable;

typedef struct {
    CARD8       byte_order;
    CARD8       protocol_version;
    CARD16      num_entries B16;
    CARD32      heap_offset B32;
} TargetsPropertyRec;

static TargetsTable*
get_target_list_table(Display* dpy) {
    Window motif_window = get_motif_window(dpy);
    TargetsTable* targets_table = NULL;
    TargetsPropertyRec* targets_property_rec_ptr;
    char* bufptr;

    unsigned char  ret;
    Atom           type;
    int            format;
    unsigned long  nitems;
    unsigned long  after;
    unsigned char  *data;
    unsigned int   i, j;

    ret = checked_XGetWindowProperty(dpy, motif_window, _XA_MOTIF_DRAG_TARGETS,
                                     0L, 100000L, False, _XA_MOTIF_DRAG_TARGETS,
                                     &type, &format, &nitems, &after,
                                     (unsigned char**)&targets_property_rec_ptr);

    if (ret != Success || type != _XA_MOTIF_DRAG_TARGETS ||
        targets_property_rec_ptr == NULL) {

        DTRACE_PRINT2("%s:%d Cannot read _XA_MOTIF_DRAG_TARGETS", __FILE__, __LINE__);
        return NULL;
    }

    if (targets_property_rec_ptr->protocol_version !=
        MOTIF_DND_PROTOCOL_VERSION) {
        DTRACE_PRINT2("%s:%d incorrect protocol version", __FILE__, __LINE__);
        return NULL;
    }

    if (targets_property_rec_ptr->byte_order != MOTIF_BYTE_ORDER) {
        SWAP2BYTES(targets_property_rec_ptr->num_entries);
        SWAP4BYTES(targets_property_rec_ptr->heap_offset);
    }

    targets_table = (TargetsTable*)malloc(sizeof(TargetsTable));
    if (targets_table == NULL) {
        DTRACE_PRINT2("%s:%d malloc failed", __FILE__, __LINE__);
        return NULL;
    }
    targets_table->num_entries = targets_property_rec_ptr->num_entries;
    targets_table->entries =
        (TargetsTableEntry*)malloc(sizeof(TargetsTableEntry) *
                                   targets_property_rec_ptr->num_entries);
    if (targets_table->entries == NULL) {
        DTRACE_PRINT2("%s:%d malloc failed", __FILE__, __LINE__);
        free(targets_table);
        return NULL;
    }

    bufptr = (char *)targets_property_rec_ptr + sizeof(TargetsPropertyRec);
    for (i = 0; i < targets_table->num_entries; i++) {
        CARD16 num_targets;
        Atom* targets;
        memcpy(&num_targets, bufptr, 2 );
        bufptr += 2;
        if (targets_property_rec_ptr->byte_order != MOTIF_BYTE_ORDER) {
            SWAP2BYTES(num_targets);
        }

        targets = (Atom*)malloc(sizeof(Atom) * num_targets);
        if (targets == NULL) {
            DTRACE_PRINT2("%s:%d malloc failed", __FILE__, __LINE__);
            free(targets_table->entries);
            free(targets_table);
            return NULL;
        }
        for (j = 0; j < num_targets; j++) {
            CARD32 target;
            memcpy(&target, bufptr, 4 );
            bufptr += 4;
            if (targets_property_rec_ptr->byte_order != MOTIF_BYTE_ORDER) {
                SWAP4BYTES(target);
            }
            targets[j] = (Atom)target;
        }

        targets_table->entries[i].num_targets = num_targets;
        targets_table->entries[i].targets = targets;
    }

    free(targets_property_rec_ptr);

    return targets_table;
}

static void
put_target_list_table(Display* dpy, TargetsTable* table) {
    Window motif_window = get_motif_window(dpy);
    TargetsPropertyRec* targets_property_rec_ptr;
    size_t table_size = sizeof(TargetsPropertyRec);
    unsigned char ret;
    int i, j;
    char* buf;

    for (i = 0; i < table->num_entries; i++) {
        table_size += table->entries[i].num_targets * sizeof(Atom) + 2;
    }

    targets_property_rec_ptr = (TargetsPropertyRec*)malloc(table_size);
    if (targets_property_rec_ptr == NULL) {
        DTRACE_PRINT2("%s:%d malloc failed", __FILE__, __LINE__);
        return;
    }
    targets_property_rec_ptr->byte_order = MOTIF_BYTE_ORDER;
    targets_property_rec_ptr->protocol_version = MOTIF_DND_PROTOCOL_VERSION;
    targets_property_rec_ptr->num_entries = table->num_entries;
    targets_property_rec_ptr->heap_offset = table_size;

    buf = (char*)targets_property_rec_ptr + sizeof(TargetsPropertyRec);

    for (i = 0; i < table->num_entries; i++) {
        CARD16 num_targets = table->entries[i].num_targets;
        memcpy(buf, &num_targets, 2);
        buf += 2;

        for (j = 0; j < num_targets; j++) {
            CARD32 target = table->entries[i].targets[j];
            memcpy(buf, &target, 4);
            buf += 4;
        }
    }

    ret = checked_XChangeProperty(dpy, motif_window, _XA_MOTIF_DRAG_TARGETS,
                                  _XA_MOTIF_DRAG_TARGETS, 8, PropModeReplace,
                                  (unsigned char*)targets_property_rec_ptr,
                                  (int)table_size);

    if (ret != Success) {
        DTRACE_PRINT2("%s:%d XChangeProperty failed", __FILE__, __LINE__);
    }

    XtFree((char*)targets_property_rec_ptr);
}

static int
_compare(const void* p1, const void* p2) {
    long diff = *(Atom*)p1 - *(Atom*)p2;

    if (diff > 0) {
        return 1;
    } else if (diff < 0) {
        return -1;
    } else {
        return 0;
    }
}

/*
 * Returns the index for the specified target list or -1 on failure.
 */
int
get_index_for_target_list(Display* dpy, Atom* targets, unsigned int num_targets) {
    TargetsTable* targets_table = NULL;
    Atom* sorted_targets = NULL;
    int i, j;
    int ret = -1;

    if (targets == NULL && num_targets > 0) {
        DTRACE_PRINT4("%s:%d targets=%X num_targets=%d",
                      __FILE__, __LINE__, targets, num_targets);
        return -1;
    }

    if (num_targets > 0) {
        sorted_targets = (Atom*)malloc(sizeof(Atom) * num_targets);
        if (sorted_targets == NULL) {
            DTRACE_PRINT2("%s:%d malloc failed.", __FILE__, __LINE__);
            return -1;
        }

        memcpy(sorted_targets, targets, sizeof(Atom) * num_targets);
        qsort ((void *)sorted_targets, (size_t)num_targets, (size_t)sizeof(Atom),
               _compare);
    }

    XGrabServer(dpy);
    targets_table = get_target_list_table(dpy);

    if (targets_table != NULL) {
        for (i = 0; i < targets_table->num_entries; i++) {
            TargetsTableEntry* entry_ptr = &targets_table->entries[i];
            Boolean equals = True;
            if (num_targets == entry_ptr->num_targets) {
                for (j = 0; j < entry_ptr->num_targets; j++) {
                    if (sorted_targets[j] != entry_ptr->targets[j]) {
                        equals = False;
                        break;
                    }
                }
            } else {
                equals = False;
            }

            if (equals) {
                XUngrabServer(dpy);
                /* Workaround for bug 5039226 */
                XSync(dpy, False);
                free((char*)sorted_targets);
                return i;
            }
        }
    } else {
        targets_table = (TargetsTable*)malloc(sizeof(TargetsTable));
        targets_table->num_entries = 0;
        targets_table->entries = NULL;
    }

    /* Index not found - expand the table. */
    targets_table->entries =
        (TargetsTableEntry*)realloc((char*)targets_table->entries,
                                    sizeof(TargetsTableEntry) *
                                    (targets_table->num_entries + 1));
    if (targets_table->entries == NULL) {
        DTRACE_PRINT2("%s:%d realloc failed.", __FILE__, __LINE__);
        XUngrabServer(dpy);
        /* Workaround for bug 5039226 */
        XSync(dpy, False);
        free((char*)sorted_targets);
        return -1;
    }

    /* Fill in the new entry */
    {
        TargetsTableEntry* new_entry =
            &targets_table->entries[targets_table->num_entries];

        new_entry->num_targets = num_targets;
        if (num_targets > 0) {
            new_entry->targets = (Atom*)malloc(sizeof(Atom) * num_targets);
            if (new_entry->targets == NULL) {
                DTRACE_PRINT2("%s:%d malloc failed.", __FILE__, __LINE__);
                XUngrabServer(dpy);
                /* Workaround for bug 5039226 */
                XSync(dpy, False);
                free((char*)sorted_targets);
                return -1;
            }
            memcpy(new_entry->targets, sorted_targets,
                   sizeof(Atom) * num_targets);
        } else {
            new_entry->targets = NULL;
        }
    }

    targets_table->num_entries++;

    put_target_list_table(dpy, targets_table);

    XUngrabServer(dpy);
    /* Workaround for bug 5039226 */
    XSync(dpy, False);

    ret = targets_table->num_entries - 1;

    free((char*)sorted_targets);

    for (i = 0; i < targets_table->num_entries; i++) {
        free((char*)targets_table->entries[i].targets);
    }

    free((char*)targets_table->entries);
    free((char*)targets_table);
    return ret;
}

/*
 * Retrieves the target list for the specified index.
 * Stores the number of targets in the list to 'num_targets' and the targets
 * to 'targets'. On failure stores 0 and NULL respectively.
 * The caller should free the allocated array when done with it.
 */
void
get_target_list_for_index(Display* dpy, int index, Atom** targets, unsigned int* num_targets) {
    TargetsTable* table = get_target_list_table(dpy);
    TargetsTableEntry* entry = NULL;

    if (table == NULL) {
        DTRACE_PRINT2("%s:%d No target table.", __FILE__, __LINE__);
        *targets = NULL;
        *num_targets = 0;
        return;
    }

    if (table->num_entries <= index) {
        DTRACE_PRINT4("%s:%d index out of bounds idx=%d entries=%d",
                      __FILE__, __LINE__, index, table->num_entries);
        *targets = NULL;
        *num_targets = 0;
        return;
    }

    entry = &table->entries[index];

    *targets = (Atom*)malloc(entry->num_targets * sizeof(Atom));

    if (*targets == NULL) {
        DTRACE_PRINT2("%s:%d malloc failed.", __FILE__, __LINE__);
        *num_targets = 0;
        return;
    }

    memcpy(*targets, entry->targets, entry->num_targets * sizeof(Atom));
    *num_targets = entry->num_targets;
}

jint
motif_to_java_actions(unsigned char motif_action) {
    jint java_action = java_awt_dnd_DnDConstants_ACTION_NONE;

    if (motif_action & MOTIF_DND_COPY) {
        java_action |= java_awt_dnd_DnDConstants_ACTION_COPY;
    }

    if (motif_action & MOTIF_DND_MOVE) {
        java_action |= java_awt_dnd_DnDConstants_ACTION_MOVE;
    }

    if (motif_action & MOTIF_DND_LINK) {
        java_action |= java_awt_dnd_DnDConstants_ACTION_LINK;
    }

    return java_action;
}

unsigned char
java_to_motif_actions(jint java_action) {
    unsigned char motif_action = MOTIF_DND_NOOP;

    if (java_action & java_awt_dnd_DnDConstants_ACTION_COPY) {
        motif_action |= MOTIF_DND_COPY;
    }

    if (java_action & java_awt_dnd_DnDConstants_ACTION_MOVE) {
        motif_action |= MOTIF_DND_MOVE;
    }

    if (java_action & java_awt_dnd_DnDConstants_ACTION_LINK) {
        motif_action |= MOTIF_DND_LINK;
    }

    return motif_action;
}

Boolean
awt_dnd_process_event(XEvent* event) {
    Boolean ret = awt_dnd_ds_process_event(event) ||
        awt_dnd_dt_process_event(event);

    /* Extract the event from the queue if it is processed. */
    if (ret) {
        XNextEvent(event->xany.display, event);
    }

    return ret;
}
