static void dndGetSelection(Window owner, Atom property) { unsigned long remaining; unsigned char *data= 0; Atom actual; int format; unsigned long count; if (Success != XGetWindowProperty(stDisplay, owner, property, 0, 65536, 1, AnyPropertyType, &actual, &format, &count, &remaining, &data)) fprintf(stderr, "dndGetSelection: XGetWindowProperty failed\n"); else if (remaining) /* a little violent perhaps */ fprintf(stderr, "dndGetSelection: XGetWindowProperty has more than 64K (why?)\n"); else { char *tokens= (char *)data; char *item; while ((item= strtok(tokens, "\n\r"))) { fdebugf((stderr, " got URI <%s>\n", item)); if (!strncmp(item, "file:", 5)) /*** xxx BOGUS -- just while image is broken ***/ addDropFile(item); tokens= 0; /* strtok is weird. this ensures more tokens, not less. */ } generateSqueakDropEventIfDroppedFiles(); fdebugf((stderr, " uxDropFileCount = %d\n", uxDropFileCount)); } XFree(data); }
static enum XdndState dndInEnter(enum XdndState state, XClientMessageEvent *evt) { fdebugf((stderr, "Receive XdndEnter (input)\n")); if (xdndEnter_version(evt) < 3) { fprintf(stderr, " xdnd: protocol version %ld not supported\n", xdndEnter_version(evt)); return state; } xdndSourceWindow= xdndEnter_sourceWindow(evt); dndGetTypeList(evt); fdebugf((stderr, " dndEnter target: 0x%lx source: 0x%lx\n", evt->window, xdndSourceWindow)); return XdndStateEntered; }
static void display_dndOutSend (char *bytes, int nbytes) { XEvent notify; XSelectionEvent *res= ¬ify.xselection; Atom targetProperty= ((None == xdndOutRequestEvent.property) ? xdndOutRequestEvent.target : xdndOutRequestEvent.property); res->type = SelectionNotify; res->display = xdndOutRequestEvent.display; res->requestor = xdndOutRequestEvent.requestor; res->selection = xdndOutRequestEvent.selection; res->target = xdndOutRequestEvent.target; res->time = xdndOutRequestEvent.time; res->send_event = True; res->property = targetProperty; /* override later if error */ XChangeProperty(stDisplay, res->requestor, targetProperty, xdndOutRequestEvent.target, 8, PropModeReplace, (unsigned char *)bytes, nbytes); XSendEvent(stDisplay, res->requestor, False, 0, ¬ify); fdebugf((stderr, "Send data for %s (output) requestor: 0x%lx\n", XGetAtomName(stDisplay, res->target), res->requestor)); }
static void *tryLoadModule(char *in, char *name) { char path[PATH_MAX], *out= path; void *handle= 0; int c; while ((c= *in++) && ':' != c) { /* copy next plugin path to path[] */ switch (c) { case '%': if ('n' == *in || 'N' == *in) { /* replace %n with name of plugin */ ++in; strcpy(out, name); out += strlen(name); continue; } if ('%' == *in) { ++in; *out++= '%'; continue; } /* fall through... */ default: *out++= c; continue; } } sprintf(out, "/" MODULE_PREFIX "%s" MODULE_SUFFIX, name); handle= dlopen(path, RTLD_NOW | RTLD_GLOBAL); fdebugf((stderr, "tryLoading(%s) = %p\n", path, handle)); if (!handle) { struct stat buf; if ((0 == stat(path, &buf)) && ! S_ISDIR(buf.st_mode)) fprintf(stderr, "%s\n", dlerror()); } return handle; }
static sqInt display_dndOutStart(char *types, int ntypes) { int pos, i; int typesSize= 0; if (xdndOutTypes != 0) { free(xdndOutTypes); xdndOutTypes= 0; } for (pos= 0; pos < ntypes; pos += strlen(types + pos) + 1) typesSize++; if (typesSize > 3) return 0; /* Supported types are up to 3 now */ xdndOutTypes= xmalloc(sizeof(Atom) * (typesSize + 1)); xdndOutTypes[typesSize]= None; for (pos= 0, i= 0; pos < ntypes; pos += strlen(types + pos) + 1, i++) xdndOutTypes[i]= XInternAtom(stDisplay, types + pos, False); for (i= 0; i < typesSize; i++) fdebugf((stderr, "dndOutStart: %s\n", XGetAtomName(stDisplay, xdndOutTypes[i]))); dndHandleEvent(DndOutStart, 0); return 1; }
/* A finished message is received. */ static enum XdndState dndOutFinished(enum XdndState state, XClientMessageEvent *evt) { fdebugf((stderr, "Receive XdndFinished (output) source: 0x%lx target: 0x%lx\n", DndWindow, xdndFinished_targetWindow(evt))); xdndOutTarget= None; return XdndStateIdle; }
/* Track the current mouse position. */ static enum XdndState dndOutMotion(enum XdndState state, XMotionEvent *evt) { Window currentWindow= None; int versionReturn= 0; if ((XdndStateOutTracking != state) && (XdndStateOutAccepted != state)) return state; currentWindow= dndAwareWindow(evt->root, evt->root, &versionReturn); if (DndWindow == currentWindow) /* Cursor is on myself */ { xdndOutTarget= None; return XdndStateOutTracking; } updateCursor(XdndStateOutAccepted == state); if ((XdndVersion > versionReturn) /* Target's version is too low. */ || (None == currentWindow)) /* I can't find XdndAware window. */ { xdndOutTarget= None; return XdndStateOutTracking; } fdebugf((stderr, "Receive MotionNotify (output) root: 0x%lx awareWindow: 0x%lx\n", evt->root, currentWindow)); if (currentWindow != xdndOutTarget) { sendLeave(xdndOutTarget, DndWindow); sendEnter(currentWindow, DndWindow); } sendPosition(currentWindow, DndWindow, evt->x_root, evt->y_root, evt->time); xdndOutTarget= currentWindow; return state; }
static char *uri2string(const char *uri) { size_t len= strlen(uri); char *string= (char *)xmalloc(len + 3); /* whoever wrote the URL stuff in the the image was too damn stupid to understand file URIs */ if (!strncmp(uri, "file:", 5)) { char *in= string, *out= string; strncpy(string, /* chop file:///absolute/path to /absolute/path */ uri + (uri[5] == '/' && uri[6] == '/' && uri[7] == '/' ? 7 : 5), len); while (*in) if ((in[0] == '%') && isxdigit(in[1]) && isxdigit(in[2])) { *out++= hexValue(in[1]) * 16 + hexValue(in[2]); in += 3; } else *out++= *in++; *out= '\0'; } else { strncpy(string, uri, len+1); } fdebugf((stderr, " uri2string: <%s>\n", string)); return string; }
static void dndGetTypeList(XClientMessageEvent *evt) { xdndWillAccept= 0; isUrlList= 0; if (xdndEnter_hasThreeTypes(evt)) { fdebugf((stderr, " 3 types\n")); updateInTypes((Atom *) xdndEnter_targets(evt), 3); } else { Atom type; int format; unsigned long count, remaining; unsigned char *data= 0; XGetWindowProperty(stDisplay, xdndSourceWindow, XdndTypeList, 0, 0x8000000L, False, XA_ATOM, &type, &format, &count, &remaining, &data); if ((type != XA_ATOM) || (format != 32) || (count == 0) || !data) { if (data) XFree(data); fprintf(stderr, "XGetWindowProperty failed in xdndGetTypeList\n"); return; } updateInTypes((Atom *) data, count); XFree(data); fdebugf((stderr, " %ld types\n", count)); } /* We only accept filenames (MIME type "text/uri-list"). */ { int i; for (i= 0; xdndInTypes[i]; ++i) { fdebugf((stderr, " type %d == %ld %s\n", i, xdndInTypes[i], XGetAtomName(stDisplay, xdndInTypes[i]))); if (XdndTextUriList == xdndInTypes[i]) { isUrlList= 1; xdndWillAccept= 1; } } } xdndWillAccept= 1; }
static enum XdndState dndOutInitialize(enum XdndState state) { fdebugf((stderr, "Internal signal DndOutStart (output)\n")); memset(&xdndOutRequestEvent, 0, sizeof(xdndOutRequestEvent)); XSetSelectionOwner(stDisplay, XdndSelection, DndWindow, CurrentTime); updateCursor(-1); return XdndStateOutTracking; }
static enum XdndState dndInFinished(enum XdndState state) { fdebugf((stderr, "Internal signal DndInFinished (input)\n")); dndSendFinished(); recordDragEvent(DragLeave, 1); dndInDestroyTypes(); return XdndStateIdle; }
static void sendDrop(Window target, Window source, Time timestamp) { long data[5]= { 0, 0, 0, 0, 0 }; data[2]= timestamp; fdebugf((stderr, "Send XdndDrop (output) source: 0x%lx target: 0x%lx\n", source, target)); sendClientMessage(data, source, target, XdndDrop); }
/* Free the module with the associated handle. Answer 0 on error (do * NOT fail the primitive!). */ sqInt ioFreeModule(void *moduleHandle) { if (dlclose(moduleHandle)) { fdebugf((stderr, "ioFreeModule(%d): %s\n", moduleHandle, dlerror())); return 0; } return 1; }
/* drastically simplified case of dndInDrop that leaves out the 8 step dance * (see http://www.newplanetsoftware.com/xdnd/). Instead grab the fileName in * the XdndSqueakLaunchDrop property and send an ack message. */ enum XdndState dndInLaunchDrop(XClientMessageEvent *evt) { Atom actualType; int actualFormat; unsigned long nitems, bytesAfter; unsigned char *fileName; unsigned int mask; fdebugf((stderr, " dndInLaunchDrop <%d> (%d)\n", evt->message_type, XdndSqueakLaunchDrop)); XGetWindowProperty(stDisplay, xdndDrop_sourceWindow(evt), XdndSqueakLaunchDrop, 0, 0x8000000L, False, XA_ATOM, &actualType, &actualFormat, &nitems, &bytesAfter, &fileName); if (nitems > 0) { int i; fdebugf((stderr, " got launch drop <%s>\n", fileName)); /* The convention is that we free the previous uxDropFileNames * and zero uxDropFileCount /before/ each dnd interchange, * which saves having to rely on consumption by the image in the * right order. But it means that if we want multiple launch * drops we're going to have to send them in one go, e.g. by * concatenating a set of null-terminated names. Too lazy now. * But, but, but. This convention means multiple launch drops * can smash previous ones. Broken. Needs more thought. */ initDropFileNames(); addDropFile(fileName); generateSqueakDropEventIfDroppedFiles(); for (i = 0; i < numLaunchDrops; i++) if (!launchDrops[i].fileName) break; if (i >= numLaunchDrops) { i = numLaunchDrops; launchDrops = xrealloc(launchDrops, ++numLaunchDrops * sizeof(*launchDrops)); } launchDrops[i].fileName = fileName; launchDrops[i].sourceWindow = xdndDrop_sourceWindow(evt); } }
static enum XdndState dndInSelectionNotify(enum XdndState state, XSelectionEvent *evt) { fdebugf((stderr, "Receive SelectionNotify (input)\n")); if (evt->property != XdndSelectionAtom) return state; dndGetSelection(evt->requestor, evt->property); dndSendFinished(); recordDragEvent(DragLeave, 1); return XdndStateIdle; }
static enum XdndState dndInPosition(enum XdndState state, XClientMessageEvent *evt) { /*fdebugf((stderr, "Receive XdndPosition (input)\n"));*/ if (xdndSourceWindow != xdndPosition_sourceWindow(evt)) { fdebugf((stderr, "dndInPosition: wrong source window\n")); return XdndStateIdle; } getMousePosition(); if ((state != XdndStateEntered) && (state != XdndStateTracking)) { fdebugf((stderr, "dndInPosition: wrong state\n")); return XdndStateIdle; } if ((state == XdndStateEntered) && xdndWillAccept) recordDragEvent(DragEnter, 1); if (xdndWillAccept) { Atom action= xdndPosition_action(evt); /*fdebugf((stderr, " dndInPosition: action = %ld %s\n", action, XGetAtomName(stDisplay, action)));*/ xdndWillAccept= (action == XdndActionMove) | (action == XdndActionCopy) | (action == XdndActionLink) | (action == XdndActionAsk); } if (xdndWillAccept) { /*fdebugf((stderr, " dndInPosition: accepting\n"));*/ dndSendStatus(1, XdndActionCopy); recordDragEvent(DragMove, 1); } else /* won't accept */ { /*fdebugf((stderr, " dndInPosition: not accepting\n"));*/ dndSendStatus(0, XdndActionPrivate); } return XdndStateTracking; }
/* Attempt to load the shared library named by the concatenation of prefix, * moduleName and suffix. Answer the new module entry, or 0 if the shared * library could not be loaded. */ static void *tryLoading(char *dirName, char *moduleName) { static char *prefixes[]= { "", "lib", 0 }; static char *suffixes[]= { "", ".so", ".dylib", 0 }; void *handle= 0; char **prefix= 0, **suffix= 0; for (prefix= prefixes; *prefix; ++prefix) for (suffix= suffixes; *suffix; ++suffix) { char libName[NAME_MAX + 32]; /* headroom for prefix/suffix */ struct stat buf; int err; sprintf(libName, "%s%s%s%s", dirName, *prefix, moduleName, *suffix); if ((err= stat(libName, &buf))) fdebugf((stderr, "cannot read: %s\n", libName)); else { if (S_ISDIR(buf.st_mode)) fdebugf((stderr, "ignoring directory: %s\n", libName)); else { fdebugf((stderr, "tryLoading %s\n", libName)); handle= dlopen(libName, RTLD_NOW | RTLD_GLOBAL); if (handle == 0) { /*if ((!err) && !(sqIgnorePluginErrors))*/ fprintf(stderr, "ioLoadModule(%s):\n %s\n", libName, dlerror()); } else { # if DEBUG printf("squeak: loaded plugin `%s'\n", libName); # endif return handle; } } } } return 0; }
enum XdndState dndInDrop(enum XdndState state, XClientMessageEvent *evt) { fdebugf((stderr, "Receive XdndDrop (input)\n")); /* If there is "text/url-list" in xdndInTypes, the selection is * processed only in DropFilesEvent. But if none (file count == 0), * the selection is handled ClipboardExtendedPlugin. */ if (isUrlList == 0) { fdebugf((stderr, " dndInDrop: no url list\n")); recordDragEvent(DragDrop, 0); return state; } dndInDestroyTypes(); if (xdndSourceWindow != xdndDrop_sourceWindow(evt)) { fdebugf((stderr, " dndInDrop: wrong source window\n")); } else if (xdndWillAccept) { Window owner; fdebugf((stderr, " dndInDrop: converting selection\n")); if (!(owner= XGetSelectionOwner(stDisplay, XdndSelection))) fprintf(stderr, " dndInDrop: XGetSelectionOwner failed\n"); else XConvertSelection(stDisplay, XdndSelection, XdndTextUriList, XdndSelectionAtom, stWindow, xdndDrop_time(evt)); initDropFileNames(); } else { fdebugf((stderr, " dndInDrop: refusing selection -- finishing\n")); } dndSendFinished(); recordDragEvent(DragLeave, 1); return XdndStateIdle; }
void *ioLoadModule(char *pluginName) { char path[PATH_MAX]; char *dir= squeakPlugins; void *handle= 0; if ((0 == pluginName) || ('\0' == pluginName[0])) { /* find module in main program */ handle= dlopen(0, RTLD_NOW | RTLD_GLOBAL); if (handle == 0) { fprintf(stderr, "ioLoadModule(<intrinsic>): %s\n", dlerror()); } else { fdebugf((stderr, "loaded: <intrinsic>\n")); } return handle; } /* try loading {pluginPaths}/MODULE_PREFIX<name>MODULE_SUFFIX */ while (*dir) { if ((handle= tryLoadModule(dir, pluginName))) return handle; while (*dir && ':' != *dir++) ; } /* try dlopen()ing LIBRARY_PREFIX<name>LIBRARY_SUFFIX searching only the default locations modulo LD_LIBRARY_PATH et al */ # if defined(HAVE_SNPRINTF) snprintf(path, sizeof(path), "%s%s%s", LIBRARY_PREFIX, pluginName, LIBRARY_SUFFIX); # else sprintf(path, "%s%s%s", LIBRARY_PREFIX, pluginName, LIBRARY_SUFFIX); # endif handle= dlopen(path, RTLD_NOW | RTLD_GLOBAL); fdebugf((stderr, "ioLoadModule(%s) = %p\n", path, handle)); return handle; }
/* The mouse button was released. */ static enum XdndState dndOutRelease(enum XdndState state, XButtonEvent *evt) { if (XdndStateIdle == state) return XdndStateIdle; fdebugf((stderr, "Receive ButtonRelease (output) window: 0x%lx\n", evt->window)); if (XdndStateOutAccepted == state) { sendDrop(xdndOutTarget, DndWindow, evt->time); return XdndStateOutAccepted; } sendLeave(xdndOutTarget, DndWindow); return XdndStateIdle; }
/* Another application is requesting the selection. */ static enum XdndState dndOutSelectionRequest(enum XdndState state, XSelectionRequestEvent *req) { fdebugf((stderr, "Receive SelectionRequest for %s (output) owner: 0x%lx : requestor: 0x%lx\n", XGetAtomName(stDisplay, req->target), req->owner, req->requestor)); if (XdndStateOutAccepted != state) { /*printf("%i is not expected in SelectionRequest\n", state);*/ return state; } memcpy(&xdndOutRequestEvent, req, sizeof(xdndOutRequestEvent)); recordDragEvent(DragRequest, 1); return state; }
/* Find a function in a loaded module. Answer 0 if not found (do NOT * fail the primitive!). */ void *ioFindExternalFunctionIn(char *lookupName, void *moduleHandle) { void *fn= dlsym(moduleHandle, lookupName); fdebugf((stderr, "ioFindExternalFunctionIn(%s, %p) = %p\n", lookupName, moduleHandle, fn)); if ((fn == 0) && (!sqIgnorePluginErrors) && strcmp(lookupName, "initialiseModule") && strcmp(lookupName, "shutdownModule") && strcmp(lookupName, "setInterpreter") && strcmp(lookupName, "getModuleName")) fprintf(stderr, "ioFindExternalFunctionIn(%s, %p):\n %s\n", lookupName, moduleHandle, dlerror()); return fn; }
static void *tryLoadingPath(char *varName, char *pluginName) { char *path= getenv(varName); void *handle= 0; if (path) { char pbuf[MAXPATHLEN]; fdebugf((stderr, "try %s=%s\n", varName, path)); strncpy(pbuf, path, sizeof(pbuf)); pbuf[sizeof(pbuf) - 1]= '\0'; for (path= strtok(pbuf, ":"); path != 0; path= strtok(0, ":")) { char buf[MAXPATHLEN]; sprintf(buf, "%s/", path); fdebugf((stderr, " path dir = %s\n", buf)); if ((handle= tryLoading(buf, pluginName)) != 0) break; } } return handle; }
static void dndSendFinished(void) { XClientMessageEvent evt; memset(&evt, 0, sizeof(evt)); evt.type = ClientMessage; evt.display = stDisplay; evt.window = xdndSourceWindow; evt.message_type = XdndFinished; evt.format = 32; xdndFinished_targetWindow(&evt)= DndWindow; XSendEvent(stDisplay, xdndSourceWindow, 0, 0, (XEvent *)&evt); fdebugf((stderr, "dndSendFinished target: 0x%lx source: 0x%lx\n", DndWindow, xdndSourceWindow)); }
/* Change cursor * TODO: The cursor should be controlled by the image, so it should be removed finally. * * state = -1 : Cursor is on Squeak window. * state = 0 : Target window doesn't accept. * state = 1 : Target window accepts. */ static void updateCursor(int state) { static int lastCursor= -1; if (lastCursor == state) return; fdebugf((stderr, "Cursor change (output) previous: %i new: %i\n", lastCursor, state)); if (1 == state) { Cursor cursor; cursor= XCreateFontCursor(stDisplay, 90); XDefineCursor(stDisplay, stWindow, cursor); } else XDefineCursor(stDisplay, stWindow, None); lastCursor= state; }
/* A status message to know accept or not is received. */ static enum XdndState dndOutStatus(enum XdndState state, XClientMessageEvent *evt) { long *ldata= evt->data.l; fdebugf((stderr, "Receive XdndStatus (output) status: 0x%lx target: 0x%lx\n", ldata[1], ldata[0])); if ((XdndStateOutTracking != state) && (XdndStateOutAccepted != state)) { /*printf("%i is not expected in XdndStatus\n", state);*/ sendLeave(ldata[0], DndWindow); return state; } if (xdndOutTarget != ldata[0]) return state; if (ldata[1] && 0x1UL) return XdndStateOutAccepted; else return XdndStateOutTracking; }
static void sendEnter(Window target, Window source) { long data[5]= { 0, 0, 0, 0, 0 }; data[1] |= 0x0UL; /* just three data types */ data[1] |= XdndVersion << 24; /* version num */ if (0 != xdndOutTypes) { data[2]= xdndOutTypes[0]; if (None != xdndOutTypes[1]) { data[3]= xdndOutTypes[1]; if (None != xdndOutTypes[2]) { data[4]= xdndOutTypes[1]; } } } fdebugf((stderr, "Send XdndEnter (output) source: 0x%lx target: 0x%lx\n", source, target)); sendClientMessage(data, source, target, XdndEnter); }
/* Find and load the named module. Answer 0 if not found (do NOT fail * the primitive!). */ void *ioLoadModule(char *pluginName) { void *handle= 0; if ((pluginName == 0) || (pluginName[0] == '\0')) { handle= dlopen(0, RTLD_NOW | RTLD_GLOBAL); if (handle == 0) fprintf(stderr, "ioLoadModule(<intrinsic>): %s\n", dlerror()); else { fdebugf((stderr, "loaded: <intrinsic>\n")); return handle; } } if (squeakPlugins) { char path[NAME_MAX]; char c, *in= squeakPlugins, *out= path; while ((c= *in++)) { if (c == '%' && ((*in == 'n') || (*in == 'N'))) { ++in; strcpy(out, pluginName); out+= strlen(pluginName); } else *out++= c; } *out= '\0'; fdebugf((stderr, "ioLoadModule plugins = %s\n path = %s\n", squeakPlugins, path)); if ((handle= tryLoading("", path))) return handle; *out++= '/'; *out= '\0'; if ((handle= tryLoading(path, pluginName))) return handle; } if (( handle= tryLoading( "./", pluginName)) || (handle= tryLoadingPath("SQUEAK_PLUGIN_PATH", pluginName)) || (handle= tryLoading( VM_LIBDIR"/", pluginName)) || (handle= tryLoadingPath("LD_LIBRARY_PATH", pluginName)) || (handle= tryLoading( "", pluginName)) # if defined(VM_X11DIR) || (handle= tryLoading(VM_X11DIR"/", pluginName)) # endif ) return handle; #if defined(DARWIN) // look in the bundle contents dir { static char *contents= 0; if (!contents) { char *delim; contents= strdup(vmPath); if ((delim= strrchr(contents, '/'))) delim[1]= '\0'; } if ((handle= tryLoading(contents, pluginName))) return handle; } // the following is needed so that, for example, the FFI can pick up // things like <cdecl: 'xyz' module: 'CoreServices'> { static char *frameworks[]= { "/System/Library/Frameworks", "/System/Library/Frameworks/CoreServices.framework/Frameworks", "/System/Library/Frameworks/ApplicationServices.framework/Frameworks", "/System/Library/Frameworks/Carbon.framework/Frameworks", 0 }; char **framework= 0; for (framework= frameworks; *framework; ++framework) { char path[NAME_MAX]; sprintf(path, "%s/%s.framework/", *framework, pluginName); if ((handle= tryLoading(path, pluginName))) return handle; } } #endif /* DARWIN */ /* finally (for VM hackers) try the pre-install build location */ { char pluginDir[MAXPATHLEN]; # ifdef HAVE_SNPRINTF snprintf(pluginDir, sizeof(pluginDir), "%s%s/", vmPath, pluginName); # else sprintf(pluginDir, "%s%s/", vmPath, pluginName); # endif if ((handle= tryLoading(pluginDir, pluginName))) return handle; # ifdef HAVE_SNPRINTF snprintf(pluginDir, sizeof(pluginDir), "%s%s/.libs/", vmPath, pluginName); # else sprintf(pluginDir, "%s%s/.libs/", vmPath, pluginName); # endif if ((handle= tryLoading(pluginDir, pluginName))) return handle; } #if DEBUG fprintf(stderr, "squeak: could not load plugin `%s'\n", pluginName); #endif return 0; }
static void sendLeave(Window target, Window source) { long data[5]= { 0, 0, 0, 0, 0 }; fdebugf((stderr, "Send XdndLeave (output) source: 0x%lx target: 0x%lx\n", source, target)); sendClientMessage(data, source, target, XdndLeave); }
static enum XdndState dndInLeave(enum XdndState state) { fdebugf((stderr, "Receive XdndLeave (input)\n")); recordDragEvent(DragLeave, 1); return XdndStateIdle; }