/*
* Motif
*
* Copyright (c) 1987-2012, The Open Group. All rights reserved.
*
* These libraries and programs are free software; you can
* redistribute them and/or modify them under the terms of the GNU
* Lesser General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* These libraries and programs are distributed in the hope that
* they will be useful, but WITHOUT ANY WARRANTY; without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU Lesser General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with these librararies and programs; if not, write
* to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
* Floor, Boston, MA 02110-1301 USA
*/
/*
* HISTORY
*/
#ifdef REV_INFO
#ifndef lint
static char rcsid[] = "$TOG: DragBS.c /main/29 1998/03/18 15:10:28 csn $"
#endif
#endif
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
/*
* (c) Copyright 1990, 1991, 1992 HEWLETT-PACKARD COMPANY */
/*****************************************************************************
*
* The purpose of the routines in this module is to cache frequently needed
* data on window properties to reduce roundtrip server requests.
*
* The data is stored on window properties of motifWindow, a persistent,
* override-redirect, InputOnly child window of the display's default root
* window. A client looks for the motifWindow id on the "_MOTIF_DRAG_WINDOW"
* property of the display's default root window. If it finds the id, the
* client saves it in its displayToMotifWindowContext. Otherwise, the
* client creates the motifWindow and stores its id on that root property
* and saves the id in its displayToMotifWindowContext. MotifWindow is
* mapped but is not visible on the screen.
*
* Two sets of data are stored on motifWindow properties:
*
* 1. an atom table, and
* 2. a targets list table.
*
* The "_MOTIF_DRAG_ATOMS" property on motifWindow contains an atoms table,
* consisting of pairs of atoms and timestamps. The atoms are interned
* once and are available for clients to use without repeated roundtrip
* server requests to intern them. A timestamp of 0 indicates that the
* atom is available. A nonzero timestamp indicates when the atom was last
* allocated to a client. The atoms table initially contains only atom
* "_MOTIF_ATOM_0" with timestamp 0. A client requests an atom by calling
* _XmAllocMotifAtom() with a timestamp. _XmAllocMotifAtom() tries to find
* an available atom in the table. If it succeeds it sets the atom's
* timestamp to the value specified and returns the atom. If no atom is
* available, _XmAllocMotifAtom() adds an atom to the table with the
* specified timestamp, updates the "_MOTIF_DRAG_ATOMS" property on
* motifWindow, and returns the new atom. These new atoms are named
* "_MOTIF_ATOM_n" where n is 1, 2, 3, ... . The routine _XmGetMotifAtom()
* returns the atom from the atoms table with nonzero timestamp less than
* but closest to a specified value. It does not change the atoms table.
* A client frees an atom by calling _XmFreeMotifAtom(), which sets the
* atom's timestamp to 0 and updates the "_MOTIF_DRAG_ATOMS" property on
* motifWindow. To minimize property access, the client saves the address
* of its current atoms table on the displayToAtomsContext context.
*
* The "_MOTIF_DRAG_TARGETS" property on motifWindow contains a targets
* table, consisting of a sequence of target lists to be shared among
* clients. These target lists are sorted into ascending order to avoid
* permutations. By sharing the targets table, clients may pass target
* lists between themselves by passing instead the corresponding target
* list indexes. The routine _XmInitTargetsTable() initializes the atoms
* table on the "_MOTIF_DRAG_ATOMS" property, then initializes the targets
* table on the "_MOTIF_DRAG_TARGETS" property to contain only two lists:
*
* { 0, }, and
* { XA_STRING, }
*
* A client adds a target list to the targets table by passing the target
* list to _XmTargetsToIndex(). _XmTargetsToIndex() first sorts the target
* list into ascending order, then searches the targets table for a match.
* If it finds a match, it returns the index of the matching targets table
* entry. Otherwise, it adds the sorted target list to the table, updates
* the "_MOTIF_DRAG_TARGETS" property on motifWindow, and returns the index
* of the new targets table entry. A client uses _XmIndexToTargets() to
* map a targets table index to a target list. To minimize property access,
* the client saves the address of its current targets table on the
* displayToTargetsContext context.
*
* The "_MOTIF_DRAG_PROXY_WINDOW" property on motifWindow contains the
* window id of the DragNDrop proxy window. The routine
* _XmGetDragProxyWindow() returns the window id stored there.
***************************************************************************/
#include <Xm/XmP.h>
#include "XmI.h"
#include "DragICCI.h"
#include "DragBSI.h"
#include "MessagesI.h"
#include <Xm/MwmUtil.h>
#include <X11/Xatom.h>
#include <X11/Xresource.h>
#include <stdio.h>
#ifndef X_NOT_STDC_ENV
#include <stdlib.h>
#endif
#undef _XmIndexToTargets
#undef _XmTargetsToIndex
#define MAXSTACK 1200
#define MAXPROPLEN 100000L
/* structures to improve portability of WriteTargets */
typedef struct {
CARD32 value B32;
} CARD32Item;
typedef struct {
CARD16 value B16;
CARD16 pad B16;
} CARD16Item;
#define MESSAGE1 _XmMMsgDragBS_0000
#define MESSAGE2 _XmMMsgDragBS_0001
#define MESSAGE3 _XmMMsgDragBS_0002
#define MESSAGE4 _XmMMsgDragBS_0003
#define MESSAGE5 _XmMMsgDragBS_0004
#define MESSAGE6 _XmMMsgDragBS_0005
#define MESSAGE7 _XmMMsgDragBS_0006
/******** Static Function Declarations ********/
static int LocalErrorHandler(
Display *display,
XErrorEvent *error) ;
static void StartProtectedSection(
Display *display,
Window window) ;
static void EndProtectedSection(
Display *display) ;
static Window GetMotifWindow(
Display *display) ;
static void SetMotifWindow(
Display *display,
Window motifWindow) ;
static xmTargetsTable GetTargetsTable(
Display *display) ;
static void SetTargetsTable(
Display *display,
xmTargetsTable targetsTable) ;
static xmAtomsTable GetAtomsTable(
Display *display) ;
static void SetAtomsTable(
Display *display,
xmAtomsTable atomsTable) ;
static Window ReadMotifWindow(
Display *display) ;
static Window CreateMotifWindow(
Display *display) ;
static void WriteMotifWindow(
Display *display,
Window *motifWindow) ;
static void WriteAtomsTable(
Display *display,
xmAtomsTable atomsTable) ;
static Boolean ReadAtomsTable(
Display *display,
xmAtomsTable atomsTable) ;
static void WriteTargetsTable(
Display *display,
xmTargetsTable targetsTable) ;
static Boolean ReadTargetsTable(
Display *display,
xmTargetsTable targetsTable) ;
static xmTargetsTable CreateDefaultTargetsTable(
Display *display) ;
static xmAtomsTable CreateDefaultAtomsTable(
Display *display) ;
static int AtomCompare(
XmConst void *atom1,
XmConst void *atom2) ;
/******** End Static Function Declarations ********/
static Boolean bad_window;
static XErrorHandler oldErrorHandler = NULL;
static unsigned long firstProtectRequest;
static Window errorWindow;
static XContext displayToMotifWindowContext = (XContext) NULL;
static XContext displayToTargetsContext = (XContext) NULL;
static XContext displayToAtomsContext = (XContext) NULL;
/*****************************************************************************
*
* LocalErrorHandler ()
*
***************************************************************************/
static int
LocalErrorHandler(
Display *display,
XErrorEvent *error )
{
int ret_val;
_XmProcessLock();
if (error->error_code == BadWindow &&
error->resourceid == errorWindow &&
error->serial >= firstProtectRequest) {
bad_window = True;
_XmProcessUnlock();
return 0;
}
if (oldErrorHandler == NULL) {
_XmProcessUnlock();
return 0; /* should never happen */
}
ret_val = (*oldErrorHandler)(display, error);
_XmProcessUnlock();
return ret_val;
}
/*****************************************************************************
*
* StartProtectedSection ()
*
* To protect against reading or writing to a property on a window that has
* been destroyed.
***************************************************************************/
static void
StartProtectedSection(
Display *display,
Window window )
{
bad_window = False;
oldErrorHandler = XSetErrorHandler (LocalErrorHandler);
firstProtectRequest = NextRequest (display);
errorWindow = window;
}
/*****************************************************************************
*
* EndProtectedSection ()
*
* Flushes any generated errors on and restores the original error handler.
***************************************************************************/
static void
EndProtectedSection(
Display *display )
{
XSync (display, False);
XSetErrorHandler (oldErrorHandler);
oldErrorHandler = NULL;
}
/*****************************************************************************
*
* GetMotifWindow ()
*
* Get the motifWindow id from the displayToMotifWindowContext.
***************************************************************************/
static Window
GetMotifWindow(
Display *display )
{
Window motifWindow;
XContext loc_context;
_XmProcessLock();
if (displayToMotifWindowContext == (XContext) NULL) {
displayToMotifWindowContext = XUniqueContext();
}
loc_context = displayToMotifWindowContext;
_XmProcessUnlock();
if (XFindContext(display,
DefaultRootWindow (display),
loc_context,
(char **)&motifWindow)) {
motifWindow = None;
}
return (motifWindow);
}
/*****************************************************************************
*
* SetMotifWindow ()
*
* Set the motifWindow id into the displayToMotifWindowContext.
***************************************************************************/
static void
SetMotifWindow(
Display *display,
Window motifWindow )
{
Window oldMotifWindow;
XContext loc_context;
_XmProcessLock();
if (displayToMotifWindowContext == (XContext) NULL) {
displayToMotifWindowContext = XUniqueContext();
}
loc_context = displayToMotifWindowContext;
_XmProcessUnlock();
/*
* Save window data.
* Delete old context if one exists.
*/
if (XFindContext (display, DefaultRootWindow (display),
loc_context,
(char **) &oldMotifWindow)) {
XSaveContext(display,
DefaultRootWindow (display),
loc_context,
(char *) motifWindow);
}
else if (oldMotifWindow != motifWindow) {
XDeleteContext (display, DefaultRootWindow (display),
loc_context);
XSaveContext(display,
DefaultRootWindow (display),
loc_context,
(char *) motifWindow);
}
}
/*****************************************************************************
*
* GetTargetsTable ()
*
* Get the targets table address from the displayToTargetsContext.
***************************************************************************/
static xmTargetsTable
GetTargetsTable(
Display *display )
{
xmTargetsTable targetsTable;
XContext loc_context;
_XmProcessLock();
if (displayToTargetsContext == (XContext) NULL) {
displayToTargetsContext = XUniqueContext();
}
loc_context = displayToTargetsContext;
_XmProcessUnlock();
if (XFindContext(display,
DefaultRootWindow (display),
loc_context,
(char **)&targetsTable)) {
targetsTable = NULL;
}
return (targetsTable);
}
/*****************************************************************************
*
* SetTargetsTable ()
*
* Set the targets table address into the displayToTargetsContext.
***************************************************************************/
static void
SetTargetsTable(
Display *display,
xmTargetsTable targetsTable )
{
xmTargetsTable oldTargetsTable;
XContext loc_context;
_XmProcessLock();
if (displayToTargetsContext == (XContext) NULL) {
displayToTargetsContext = XUniqueContext();
}
loc_context = displayToTargetsContext;
_XmProcessUnlock();
/*
* Save targets data.
* Delete old context if one exists.
*/
if (XFindContext (display, DefaultRootWindow (display),
loc_context,
(char **) &oldTargetsTable)) {
if (targetsTable)
XSaveContext(display,
DefaultRootWindow (display),
loc_context,
(char *) targetsTable);
}
else if (oldTargetsTable != targetsTable) {
XDeleteContext (display, DefaultRootWindow (display),
loc_context);
{
unsigned i = 0 ;
while( i < oldTargetsTable->numEntries )
{
XtFree( (char *) oldTargetsTable->entries[i++].targets) ;
}
XtFree( (char *) oldTargetsTable->entries) ;
XtFree( (char *) oldTargetsTable) ;
}
if (targetsTable)
XSaveContext(display,
DefaultRootWindow (display),
loc_context,
(char *) targetsTable);
}
}
/*****************************************************************************
*
* GetAtomsTable ()
*
* Get the atomsTable address from the displayToAtomsContext.
***************************************************************************/
static xmAtomsTable
GetAtomsTable(
Display *display )
{
xmAtomsTable atomsTable;
XContext loc_context;
_XmProcessLock();
if (displayToAtomsContext == (XContext) NULL) {
displayToAtomsContext = XUniqueContext();
}
loc_context = displayToAtomsContext;
_XmProcessUnlock();
if (XFindContext (display,
DefaultRootWindow (display),
loc_context,
(XPointer *)&atomsTable)) {
atomsTable = NULL;
}
return (atomsTable);
}
/*****************************************************************************
*
* SetAtomsTable ()
*
* Set the atoms table address into the displayToAtomsContext.
***************************************************************************/
static void
SetAtomsTable(
Display *display,
xmAtomsTable atomsTable )
{
xmAtomsTable oldAtomsTable;
XContext loc_context;
_XmProcessLock();
if (displayToAtomsContext == (XContext) NULL) {
displayToAtomsContext = XUniqueContext();
}
loc_context = displayToAtomsContext;
_XmProcessUnlock();
/*
* Save atom data.
* Delete old context if one exists.
*/
if (XFindContext (display, DefaultRootWindow (display),
loc_context,
(char **) &oldAtomsTable)) {
if (atomsTable)
XSaveContext(display,
DefaultRootWindow (display),
loc_context,
(char *) atomsTable);
}
else if (oldAtomsTable != atomsTable) {
XDeleteContext (display, DefaultRootWindow (display),
loc_context);
XtFree( (char *) (oldAtomsTable->entries)) ;
XtFree( (char *) oldAtomsTable) ;
if (atomsTable)
XSaveContext(display,
DefaultRootWindow (display),
loc_context,
(char *) atomsTable);
}
}
/*****************************************************************************
*
* ReadMotifWindow ()
*
***************************************************************************/
static Boolean RMW_ErrorFlag;
/*ARGSUSED*/
static int
RMW_ErrorHandler(Display *display, /* unused */
XErrorEvent* event) /* unused */
{
_XmProcessLock();
RMW_ErrorFlag = True;
_XmProcessUnlock();
return 0 ; /* unused */
}
static Window
ReadMotifWindow(
Display *display )
{
Atom motifWindowAtom;
Atom type;
int format;
unsigned long lengthRtn;
unsigned long bytesafter;
Window *property = NULL;
Window motifWindow = None;
XErrorHandler old_Handler;
/* Setup error proc and reset error flag */
old_Handler = XSetErrorHandler((XErrorHandler) RMW_ErrorHandler);
_XmProcessLock();
RMW_ErrorFlag = False;
_XmProcessUnlock();
motifWindowAtom = XInternAtom (display, XmI_MOTIF_DRAG_WINDOW, False);
if ((XGetWindowProperty (display,
RootWindow (display, 0),
motifWindowAtom,
0L, MAXPROPLEN,
False,
AnyPropertyType,
&type,
&format,
&lengthRtn,
&bytesafter,
(unsigned char **) &property) == Success) &&
(type == XA_WINDOW) &&
(format == 32) &&
(lengthRtn == 1)) {
motifWindow = *property;
}
if (property) {
XFree ((char *)property);
}
XSetErrorHandler(old_Handler);
_XmProcessLock();
if (RMW_ErrorFlag) motifWindow = None;
_XmProcessUnlock();
return (motifWindow);
}
/*****************************************************************************
*
* CreateMotifWindow ()
*
* Creates a persistent window to hold the target list and atom pair
* properties. This window is not visible on the screen.
*
***************************************************************************/
static Window
CreateMotifWindow(
Display *display )
{
Display *ndisplay;
XSetWindowAttributes sAttributes;
Window motifWindow;
if ((ndisplay = XOpenDisplay(XDisplayString(display))) == NULL) {
XmeWarning( (Widget) XmGetXmDisplay (display), MESSAGE3);
return None;
}
XGrabServer(ndisplay);
XSetCloseDownMode (ndisplay, RetainPermanent);
sAttributes.override_redirect = True;
sAttributes.event_mask = PropertyChangeMask;
motifWindow = XCreateWindow (ndisplay,
DefaultRootWindow (ndisplay),
-100, -100, 10, 10, 0, 0,
InputOnly,
CopyFromParent,
(CWOverrideRedirect |CWEventMask),
&sAttributes);
XMapWindow (ndisplay, motifWindow);
WriteMotifWindow (ndisplay, &motifWindow);
XCloseDisplay(ndisplay);
return (motifWindow);
}
/*****************************************************************************
*
* WriteMotifWindow ()
*
***************************************************************************/
static void
WriteMotifWindow(
Display *display,
Window *motifWindow )
{
Atom motifWindowAtom;
motifWindowAtom = XInternAtom (display, XmI_MOTIF_DRAG_WINDOW, False);
XChangeProperty (display,
RootWindow (display, 0),
motifWindowAtom,
XA_WINDOW,
32,
PropModeReplace,
(unsigned char *) motifWindow,
1);
}
/*****************************************************************************
*
* WriteAtomsTable ()
*
***************************************************************************/
static void
WriteAtomsTable(
Display *display,
xmAtomsTable atomsTable )
{
BYTE stackData[MAXSTACK];
struct _propertyRec {
xmMotifAtomsPropertyRec info;
xmMotifAtomsTableRec entry[1];
} *propertyRecPtr;
Atom atomsTableAtom;
int i;
Window motifWindow;
size_t dataSize;
if (!atomsTable) {
XmeWarning( (Widget) XmGetXmDisplay (display), MESSAGE4);
return;
}
/* If the data is bigger than the default stack allocation, then
* allocate heap storage, else use automatic storage.
*/
dataSize = sizeof(xmMotifAtomsPropertyRec) +
atomsTable->numEntries * sizeof(xmMotifAtomsTableRec) ;
if ( dataSize > MAXSTACK ) {
propertyRecPtr = (struct _propertyRec *)XtMalloc( dataSize );
} else {
propertyRecPtr = (struct _propertyRec *)stackData;
}
propertyRecPtr->info.byte_order = (BYTE) _XmByteOrderChar;
propertyRecPtr->info.protocol_version = (BYTE) _MOTIF_DRAG_PROTOCOL_VERSION;
propertyRecPtr->info.num_atoms = atomsTable->numEntries;
propertyRecPtr->info.heap_offset = dataSize;
/* write each entry's atom and time */
for (i = 0; i < atomsTable->numEntries; i++) {
propertyRecPtr->entry[i].atom = atomsTable->entries[i].atom;
propertyRecPtr->entry[i].time = atomsTable->entries[i].time;
}
/*
* Write the buffer to the property within a protected section.
*/
atomsTableAtom = XInternAtom (display, XmI_MOTIF_DRAG_ATOMS, False);
motifWindow = GetMotifWindow (display);
_XmProcessLock();
StartProtectedSection (display, motifWindow);
XChangeProperty (display,
motifWindow,
atomsTableAtom,
atomsTableAtom,
8,
PropModeReplace,
(unsigned char *)propertyRecPtr,
dataSize );
/* If we had to use a heap buffer, free it. */
if (propertyRecPtr != (struct _propertyRec *)stackData) {
XtFree((char *)propertyRecPtr);
}
EndProtectedSection (display);
if (bad_window) {
XmeWarning( (Widget) XmGetXmDisplay (display), MESSAGE1);
}
_XmProcessUnlock();
}
/*****************************************************************************
*
* ReadAtomsTable ()
*
***************************************************************************/
static Boolean
ReadAtomsTable(
Display *display,
xmAtomsTable atomsTable )
{
struct {
xmMotifAtomsPropertyRec info;
xmMotifAtomsTableRec entry[1];
} *propertyRecPtr = NULL;
Atom atomsTableAtom;
int format;
unsigned long bytesafter, lengthRtn;
Atom type;
int i;
Boolean ret;
Window motifWindow;
atomsTableAtom = XInternAtom (display, XmI_MOTIF_DRAG_ATOMS, False);
motifWindow = GetMotifWindow (display);
_XmProcessLock();
StartProtectedSection (display, motifWindow);
ret = ((XGetWindowProperty (display, /* display* */
motifWindow, /* window */
atomsTableAtom, /* property atom */
0L, MAXPROPLEN, /* long_offset, long_length */
False, /* delete flag */
atomsTableAtom, /* property type */
&type, /* returned actual type */
&format, /* returned actual format */
&lengthRtn, /* returned item count */
&bytesafter, /* returned bytes remaining */
(unsigned char **) &propertyRecPtr)
/* returned data */
== Success) &&
(lengthRtn >= sizeof(xmMotifAtomsPropertyRec)));
EndProtectedSection (display);
if (bad_window) {
static Boolean first_time = True;
/*
* Try to recreate the motifWindow. We could have gotten an invalid
* window id from the _MOTIF_DRAG_WINDOW property.
*/
if (first_time) {
motifWindow = CreateMotifWindow (display);
SetMotifWindow (display, motifWindow);
first_time = False;
} else
XmeWarning( (Widget) XmGetXmDisplay (display), MESSAGE1);
ret = False;
}
_XmProcessUnlock();
if (ret) {
if (propertyRecPtr->info.protocol_version !=
_MOTIF_DRAG_PROTOCOL_VERSION) {
XmeWarning( (Widget) XmGetXmDisplay (display), MESSAGE2);
}
if (propertyRecPtr->info.byte_order != _XmByteOrderChar) {
swap2bytes(propertyRecPtr->info.num_atoms);
swap4bytes(propertyRecPtr->info.heap_offset);
}
if (atomsTable == NULL)
{
atomsTable = (xmAtomsTable) XtMalloc(sizeof(xmAtomsTableRec));
atomsTable->numEntries = 0;
atomsTable->entries = NULL;
SetAtomsTable (display, atomsTable);
}
if (propertyRecPtr->info.num_atoms > atomsTable->numEntries) {
/*
* expand the atoms table
*/
atomsTable->entries = (xmAtomsTableEntry) XtRealloc(
(char *)atomsTable->entries, /* NULL ok */
sizeof(xmAtomsTableEntryRec) * propertyRecPtr->info.num_atoms);
}
/*
* Read the atom table entries.
*/
for (i = 0; i < (int)propertyRecPtr->info.num_atoms; i++) {
if (propertyRecPtr->info.byte_order != _XmByteOrderChar) {
swap4bytes(propertyRecPtr->entry[i].atom);
swap4bytes(propertyRecPtr->entry[i].time);
}
atomsTable->entries[i].atom = (Atom) propertyRecPtr->entry[i].atom;
atomsTable->entries[i].time = (Time) propertyRecPtr->entry[i].time;
}
atomsTable->numEntries = propertyRecPtr->info.num_atoms;
}
/*
* Free any memory that Xlib passed us.
*/
if (propertyRecPtr) {
XFree((char *)propertyRecPtr);
}
return (ret);
}
/*****************************************************************************
*
* WriteTargetsTable ()
*
***************************************************************************/
static void
WriteTargetsTable(
Display *display,
xmTargetsTable targetsTable )
{
BYTE stackData[MAXSTACK], *fill;
struct _propertyRec {
xmMotifTargetsPropertyRec info;
} *propertyRecPtr;
Atom targetsTableAtom;
int i, j;
Window motifWindow;
size_t dataSize;
CARD16Item shortItem;
CARD32Item longItem;
if (!targetsTable) {
XmeWarning( (Widget) XmGetXmDisplay (display), MESSAGE5);
return;
}
/* Calculate the total size of the property. */
dataSize = sizeof(xmMotifTargetsPropertyRec);
for (i = 0; i < targetsTable->numEntries; i++) {
dataSize += targetsTable->entries[i].numTargets * 4 + 2;
}
/* If size needed is bigger than the pre-allocated space, allocate a
* bigger buffer.
*/
if ( dataSize > MAXSTACK ){
propertyRecPtr = (struct _propertyRec *)XtMalloc( dataSize );
} else {
propertyRecPtr = (struct _propertyRec *)stackData ;
}
propertyRecPtr->info.byte_order = (BYTE) _XmByteOrderChar;
propertyRecPtr->info.protocol_version = (BYTE) _MOTIF_DRAG_PROTOCOL_VERSION;
propertyRecPtr->info.num_target_lists = targetsTable->numEntries;
propertyRecPtr->info.heap_offset = dataSize;
/* write each target list's count and atoms */
fill = (BYTE *)propertyRecPtr + sizeof(xmMotifTargetsPropertyRec);
for (i = 0; i < targetsTable->numEntries; i++) {
shortItem.value = targetsTable->entries[i].numTargets;
memcpy( fill, &shortItem, 2 );
fill += 2;
/*
* Write each Atom out one at a time as a CARD32.
*/
for (j = 0; j < targetsTable->entries[i].numTargets; j++) {
longItem.value = targetsTable->entries[i].targets[j];
memcpy( fill, &longItem, 4 );
fill += 4;
}
}
/*
* Write the buffer to the property within a protected section.
*/
targetsTableAtom = XInternAtom (display, XmI_MOTIF_DRAG_TARGETS, False);
motifWindow = GetMotifWindow (display);
_XmProcessLock();
StartProtectedSection (display, motifWindow);
XChangeProperty (display,
motifWindow,
targetsTableAtom,
targetsTableAtom,
8,
PropModeReplace,
(unsigned char *)propertyRecPtr,
dataSize);
/* If a buffer was allocated, free it. */
if (propertyRecPtr != (struct _propertyRec *)stackData) {
XtFree((char *)propertyRecPtr);
}
EndProtectedSection (display);
if (bad_window) {
XmeWarning( (Widget) XmGetXmDisplay (display), MESSAGE1);
}
_XmProcessUnlock();
}
/*****************************************************************************
*
* ReadTargetsTable ()
*
***************************************************************************/
static Boolean
ReadTargetsTable(
Display *display,
xmTargetsTable targetsTable )
{
struct _propertyRec {
xmMotifTargetsPropertyRec info;
} *propertyRecPtr = NULL;
char *bufptr;
short num_targets;
Atom targetsTableAtom;
int format;
unsigned long bytesafter, lengthRtn;
Atom type;
int i, j;
Atom *targets;
Boolean ret;
Window motifWindow;
CARD16Item shortItem;
CARD32Item longItem;
targetsTableAtom = XInternAtom (display, XmI_MOTIF_DRAG_TARGETS, False);
motifWindow = GetMotifWindow (display);
_XmProcessLock();
StartProtectedSection (display, motifWindow);
ret = ((XGetWindowProperty (display,
motifWindow,
targetsTableAtom,
0L, MAXPROPLEN,
False,
targetsTableAtom,
&type,
&format,
&lengthRtn,
&bytesafter,
(unsigned char **) &propertyRecPtr) == Success) &&
(lengthRtn >= sizeof(xmMotifTargetsPropertyRec)));
EndProtectedSection (display);
if (bad_window) {
XmeWarning( (Widget) XmGetXmDisplay (display), MESSAGE1);
ret = False;
}
_XmProcessUnlock();
if (ret) {
if (propertyRecPtr->info.protocol_version !=
_MOTIF_DRAG_PROTOCOL_VERSION) {
XmeWarning( (Widget) XmGetXmDisplay (display), MESSAGE2);
}
if (propertyRecPtr->info.byte_order != _XmByteOrderChar) {
swap2bytes(propertyRecPtr->info.num_target_lists);
swap4bytes(propertyRecPtr->info.heap_offset);
}
if (targetsTable == NULL)
{
targetsTable = (xmTargetsTable)XtMalloc(sizeof(xmTargetsTableRec));
targetsTable->numEntries = 0;
targetsTable->entries = NULL;
SetTargetsTable (display, targetsTable);
}
if (propertyRecPtr->info.num_target_lists > targetsTable->numEntries) {
/*
* expand the target table
*/
targetsTable->entries = (xmTargetsTableEntry)
XtRealloc(
(char *)targetsTable->entries, /* NULL ok */
sizeof(xmTargetsTableEntryRec) *
propertyRecPtr->info.num_target_lists);
/*
* read the new entries
*/
bufptr = (char *)propertyRecPtr + sizeof(xmMotifTargetsPropertyRec);
for (i = 0; i < targetsTable->numEntries; i++) {
memcpy( &shortItem, bufptr, 2 );
if (propertyRecPtr->info.byte_order != _XmByteOrderChar) {
swap2bytes(shortItem.value);
}
num_targets = shortItem.value;
bufptr += 2 + 4 * num_targets;
if (num_targets != targetsTable->entries[i].numTargets) {
XmeWarning( (Widget) XmGetXmDisplay (display), MESSAGE6);
}
}
for (; i < (int)propertyRecPtr->info.num_target_lists; i++) {
memcpy( &shortItem, bufptr, 2 );
bufptr += 2;
if (propertyRecPtr->info.byte_order != _XmByteOrderChar) {
swap2bytes(shortItem.value);
}
num_targets = shortItem.value;
if(!num_targets)
targets = NULL;
else
targets = (Atom *) XtMalloc(sizeof(Atom) * num_targets);
/*
* Read each Atom in one at a time.
*/
for (j = 0; j < num_targets; j++) {
memcpy( &longItem, bufptr, 4 );
bufptr += 4;
if (propertyRecPtr->info.byte_order != _XmByteOrderChar) {
swap4bytes(longItem.value);
}
targets[j] = (Atom) longItem.value ;
}
targetsTable->numEntries++;
targetsTable->entries[i].numTargets = num_targets;
targetsTable->entries[i].targets = targets;
}
}
}
/*
* Free any memory that Xlib passed us.
*/
if (propertyRecPtr) {
XFree((char *)propertyRecPtr);
}
return (ret);
}
/*****************************************************************************
*
* CreateDefaultTargetsTable ()
*
* Create the default targets table.
***************************************************************************/
static Atom nullTargets[] = { 0, };
static Atom stringTargets[] = { XA_STRING, };
static xmTargetsTable
CreateDefaultTargetsTable(
Display *display )
{
xmTargetsTable targetsTable;
Cardinal size;
targetsTable = (xmTargetsTable) XtMalloc(sizeof(xmTargetsTableRec));
targetsTable->numEntries = 2;
targetsTable->entries =
(xmTargetsTableEntry) XtMalloc(sizeof(xmTargetsTableEntryRec) * 2);
/* According to specs and man pages, default values of XmNimportTargets
* and XmNnumImportTargets should be NULL and zero respectively. The
* corresponding record in default targets table is entries[0]. The original
* setting of entries[0] (commented out below) was resulting in to default
* values of non NULL pointer pointing to target list with NULL entry and
* 1 for XmNimportTargets and XmNnumImportTargets respectively.
*
* The changed code set the XmNnumImportTargets to 0 and XmNimportTargets to
* NULL. This change may cause problem in code where importTarget
* is dereferanced twice without checking for possible NULL value. It that
* happens, and there is no easy way to fix it, change entries[0].targets to
* nullTargets, but leave entries[0].numTargets to 0.
*/
#if 0
targetsTable->entries[0].numTargets = XtNumber(nullTargets);
size = sizeof(Atom) * targetsTable->entries[0].numTargets;
targetsTable->entries[0].targets = (Atom*) XtMalloc(size);
memcpy(targetsTable->entries[0].targets, nullTargets, size);
#endif
targetsTable->entries[0].numTargets = _XmDefaultNumImportTargets;
targetsTable->entries[0].targets = (Atom*) _XmDefaultImportTargets;
targetsTable->entries[1].numTargets = XtNumber(stringTargets);
size = sizeof(Atom) * targetsTable->entries[1].numTargets;
targetsTable->entries[1].targets = (Atom*) XtMalloc(size);
memcpy(targetsTable->entries[1].targets, stringTargets, size);
SetTargetsTable (display, targetsTable);
return (targetsTable);
}
/*****************************************************************************
*
* CreateDefaultAtomsTable ()
*
* Create the default atoms table.
***************************************************************************/
static xmAtomsTable
CreateDefaultAtomsTable(
Display *display )
{
xmAtomsTable atomsTable;
atomsTable = (xmAtomsTable) XtMalloc(sizeof(xmAtomsTableRec));
atomsTable->numEntries = 1;
atomsTable->entries =
(xmAtomsTableEntry) XtMalloc(sizeof(xmAtomsTableEntryRec));
atomsTable->entries[0].atom =
XInternAtom (display, XmS_MOTIF_ATOM_0, False);
atomsTable->entries[0].time = 0;
SetAtomsTable (display, atomsTable);
return (atomsTable);
}
/*****************************************************************************
*
* _XmInitTargetsTable ()
*
***************************************************************************/
void
_XmInitTargetsTable(
Display *display )
{
Window motifWindow;
Boolean grabbed = False;
/*
* Read the motifWindow property on the root. If the property is not
* there, create a persistant motifWindow and put it on the property.
* Reading the atom pair, atoms table, and targets table properties
* on motifWindow is delayed so they can be saved in contexts indexed
* by the original display.
*/
if ((motifWindow = ReadMotifWindow (display)) == None) {
motifWindow = CreateMotifWindow (display);
}
SetMotifWindow (display, motifWindow);
/*
* At this time, we are not sure the motifWindow id is valid,
* but we will find out in the ReadAtomsTable. We will try to
* recreate it there.
*/
if (!ReadAtomsTable (display, GetAtomsTable (display))) {
XGrabServer(display);
grabbed = True;
if (!ReadAtomsTable (display, GetAtomsTable (display))) {
WriteAtomsTable (display, CreateDefaultAtomsTable (display));
}
}
if (!ReadTargetsTable (display, GetTargetsTable (display))) {
if (!grabbed) {
XGrabServer(display);
grabbed = True;
if (!ReadTargetsTable (display, GetTargetsTable (display))) {
WriteTargetsTable (display,
CreateDefaultTargetsTable (display));
}
}
else {
WriteTargetsTable (display, CreateDefaultTargetsTable (display));
}
}
if (grabbed) {
XUngrabServer (display);
XFlush (display);
}
}
void _XmClearDisplayTables (Display *display)
{
SetAtomsTable(display,NULL);
SetTargetsTable(display,NULL);
}
/*****************************************************************************
*
* _XmIndexToTargets ()
*
***************************************************************************/
Cardinal
_XmIndexToTargets(
Widget shell,
Cardinal t_index,
Atom **targetsRtn )
{
Display *display = XtDisplay (shell);
xmTargetsTable targetsTable;
if (!(targetsTable = GetTargetsTable (display))) {
_XmInitTargetsTable (display);
targetsTable = GetTargetsTable (display);
}
if (t_index >= targetsTable->numEntries) {
/*
* Retrieve the targets table from motifWindow.
* If this fails, then either the motifWindow or the targets table
* property on motifWindow has been destroyed, so reinitialize.
*/
if (!ReadTargetsTable (display, targetsTable)) {
_XmInitTargetsTable (display);
targetsTable = GetTargetsTable (display);
}
}
if (t_index >= targetsTable->numEntries) {
XmeWarning ((Widget) XmGetXmDisplay (display), MESSAGE7);
*targetsRtn = NULL;
return 0;
}
*targetsRtn = targetsTable->entries[t_index].targets;
return targetsTable->entries[t_index].numTargets;
}
/*****************************************************************************
*
* _XmAtomCompare ()
*
* The routine must return an integer less than, equal to, or greater than
* 0 according as the first argument is to be considered less
* than, equal to, or greater than the second.
***************************************************************************/
static int
AtomCompare(
XmConst void *atom1,
XmConst void *atom2 )
{
return (*((Atom *) atom1) - *((Atom *) atom2));
}
/*****************************************************************************
*
* _XmTargetsToIndex ()
*
***************************************************************************/
Cardinal
_XmTargetsToIndex(
Widget shell,
Atom *targets,
Cardinal numTargets )
{
Display *display = XtDisplay (shell);
Cardinal i, j;
Cardinal size;
Cardinal oldNumEntries;
Atom *newTargets;
xmTargetsTable targetsTable;
if(!numTargets) return 0;
_XmProcessLock();
if (!(targetsTable = GetTargetsTable (display))) {
_XmInitTargetsTable (display);
targetsTable = GetTargetsTable (display);
}
/*
* Create a new targets list, sorted in ascending order.
*/
size = sizeof(Atom) * numTargets;
newTargets = (Atom *) XtMalloc(size);
memcpy (newTargets, targets, size);
qsort ((void *)newTargets, (size_t)numTargets, (size_t)sizeof(Atom),
AtomCompare);
/*
* Try to find the targets list in the targets table.
*/
for (i = 0; i < targetsTable->numEntries; i++) {
if (numTargets == targetsTable->entries[i].numTargets) {
for (j = 0; j < numTargets; j++) {
if (newTargets[j] != targetsTable->entries[i].targets[j]) {
break;
}
}
if (j == numTargets) {
XtFree ((char *)newTargets);
_XmProcessUnlock();
return i;
}
}
}
oldNumEntries = targetsTable->numEntries;
/*
* Lock and retrieve the target table from motifWindow.
* If this fails, then either the motifWindow or the targets table
* property on motifWindow has been destroyed, so reinitialize.
* If the target list is still not in the table, add the target list
* to the table and write the table out to its property.
*/
XGrabServer (display);
if (!ReadTargetsTable (display, targetsTable)) {
XUngrabServer (display);
_XmInitTargetsTable (display);
XGrabServer (display);
targetsTable = GetTargetsTable (display);
}
for (i = oldNumEntries; i < targetsTable->numEntries; i++) {
if (numTargets == targetsTable->entries[i].numTargets) {
for (j = 0; j < numTargets; j++) {
if (newTargets[j] != targetsTable->entries[i].targets[j]) {
break;
}
}
if (j == numTargets) {
XtFree ((char *)newTargets);
break;
}
}
}
if (i == targetsTable->numEntries) {
targetsTable->numEntries++;
targetsTable->entries = (xmTargetsTableEntry) XtRealloc(
(char *)targetsTable->entries, /* NULL ok */
sizeof(xmTargetsTableEntryRec) * (targetsTable->numEntries));
targetsTable->entries[i].numTargets = numTargets;
targetsTable->entries[i].targets = newTargets;
WriteTargetsTable (display, targetsTable);
}
XUngrabServer (display);
XFlush (display);
_XmProcessUnlock();
return i;
}
/*****************************************************************************
*
* _XmAllocMotifAtom ()
*
* Allocate an atom in the atoms table with the specified time stamp.
***************************************************************************/
Atom
_XmAllocMotifAtom(
Widget shell,
Time time )
{
Display *display = XtDisplay (shell);
xmAtomsTable atomsTable;
xmAtomsTableEntry p;
Cardinal i;
char atomname[80];
Atom atomReturn = None;
if (!(atomsTable = GetAtomsTable (display))) {
_XmInitTargetsTable (display);
atomsTable = GetAtomsTable (display);
}
/*
* Lock and retrieve the atoms table from motifWindow.
* If this fails, then either the motifWindow or the atoms table
* property on motifWindow has been destroyed, so reinitialize.
* Try to find an available atom in the table (time == 0).
* If no atom is available, add an atom to the table.
* Write the atoms table out to its property.
*/
XGrabServer (display);
if (!ReadAtomsTable (display, atomsTable)) {
XUngrabServer (display);
_XmInitTargetsTable (display);
XGrabServer (display);
atomsTable = GetAtomsTable (display);
}
for (p = atomsTable->entries, i = atomsTable->numEntries; i; p++, i--) {
if ((p->time) == 0) {
p->time = time;
atomReturn = p->atom;
break;
}
}
if (atomReturn == None) {
i = atomsTable->numEntries++;
atomsTable->entries = (xmAtomsTableEntry) XtRealloc(
(char *)atomsTable->entries, /* NULL ok */
(atomsTable->numEntries * sizeof(xmAtomsTableEntryRec)));
sprintf(atomname, "%s%d", "_MOTIF_ATOM_", i);
atomsTable->entries[i].atom = XInternAtom (display, atomname, False);
atomsTable->entries[i].time = time;
atomReturn = atomsTable->entries[i].atom;
}
WriteAtomsTable (display, atomsTable);
XUngrabServer (display);
XFlush (display);
return (atomReturn);
}
/*****************************************************************************
*
* _XmGetMotifAtom ()
*
* Get the atom from the atoms table with nonzero timestamp less than but
* closest to the specified value.
***************************************************************************/
Atom
_XmGetMotifAtom(
Widget shell,
Time time )
{
Display *display = XtDisplay (shell);
xmAtomsTable atomsTable;
Cardinal i;
Atom atomReturn = None;
Time c_time;
/*
* Get the atoms table saved in the display's context.
* This table will be updated from the motifWindow property.
*/
if (!(atomsTable = GetAtomsTable (display))) {
_XmInitTargetsTable (display);
atomsTable = GetAtomsTable (display);
}
/*
* Lock and retrieve the atoms table from motifWindow.
* If this fails, then either the motifWindow or the atoms table
* property on motifWindow has been destroyed, so reinitialize.
* Try to find the atom with nonzero timestamp less than but closest
* to the specified value.
*/
XGrabServer (display);
if (!ReadAtomsTable (display, atomsTable)) {
XUngrabServer (display);
_XmInitTargetsTable (display);
XGrabServer (display);
atomsTable = GetAtomsTable (display);
}
for (i = 0; i < atomsTable->numEntries; i++) {
if ((atomsTable->entries[i].time) &&
(atomsTable->entries[i].time <= time)) {
break;
}
}
if (i < atomsTable->numEntries) {
atomReturn = atomsTable->entries[i].atom;
c_time = atomsTable->entries[i++].time;
for (; i < atomsTable->numEntries; i++) {
if ((atomsTable->entries[i].time > c_time) &&
(atomsTable->entries[i].time < time)) {
atomReturn = atomsTable->entries[i].atom;
c_time = atomsTable->entries[i].time;
}
}
}
XUngrabServer (display);
XFlush (display);
return (atomReturn);
}
/*****************************************************************************
*
* _XmFreeMotifAtom ()
*
* Free an atom in the atoms table by giving it a zero timestamp.
***************************************************************************/
void
_XmFreeMotifAtom(
Widget shell,
Atom atom )
{
Display *display = XtDisplay (shell);
xmAtomsTable atomsTable;
xmAtomsTableEntry p;
Cardinal i;
if (atom == None) {
return;
}
/*
* Get the atoms table saved in the display's context.
* This table will be updated from the motifWindow property.
*/
if (!(atomsTable = GetAtomsTable (display))) {
_XmInitTargetsTable (display);
atomsTable = GetAtomsTable (display);
}
/*
* Lock and retrieve the atoms table from its property.
* If this fails, then either the motifWindow or the atoms table
* property on motifWindow has been destroyed, so reinitialize.
* Free the matched atom, if present, and write the atoms table out
* to its property.
*/
XGrabServer (display);
if (!ReadAtomsTable (display, atomsTable)) {
XUngrabServer (display);
_XmInitTargetsTable (display);
XGrabServer (display);
atomsTable = GetAtomsTable (display);
}
for (p = atomsTable->entries, i = atomsTable->numEntries; i; p++, i--) {
if (p->atom == atom) {
p->time = (Time) 0;
WriteAtomsTable (display, atomsTable);
break;
}
}
XUngrabServer (display);
XFlush (display);
}
/*****************************************************************************
*
* _XmDestroyMotifWindow ()
*
***************************************************************************/
void
_XmDestroyMotifWindow(
Display *display )
{
Window motifWindow;
Atom motifWindowAtom;
if ((motifWindow = ReadMotifWindow (display)) != None) {
motifWindowAtom = XInternAtom (display, XmI_MOTIF_DRAG_WINDOW, False);
XDeleteProperty (display,
DefaultRootWindow (display),
motifWindowAtom);
XDestroyWindow (display, motifWindow);
}
}
/*****************************************************************************
*
* _XmGetDragProxyWindow ()
*
***************************************************************************/
Window
_XmGetDragProxyWindow(
Display *display )
{
Atom motifProxyWindowAtom;
Atom type;
int format;
unsigned long lengthRtn;
unsigned long bytesafter;
Window *property = NULL;
Window motifWindow;
Window motifProxyWindow = None;
if ((motifWindow = ReadMotifWindow (display)) != None) {
motifProxyWindowAtom =
XInternAtom (display, XmI_MOTIF_DRAG_PROXY_WINDOW, False);
_XmProcessLock();
StartProtectedSection (display, motifWindow);
if ((XGetWindowProperty (display,
motifWindow,
motifProxyWindowAtom,
0L, MAXPROPLEN,
False,
AnyPropertyType,
&type,
&format,
&lengthRtn,
&bytesafter,
(unsigned char **) &property) == Success) &&
(type == XA_WINDOW) &&
(format == 32) &&
(lengthRtn == 1)) {
motifProxyWindow = *property;
}
EndProtectedSection (display);
_XmProcessUnlock();
if (property) {
XFree ((char *)property);
}
}
return (motifProxyWindow);
}