wings: phase γ (dragcommon) — split X11 XDND wire code into _x11

dragcommon.c now contains only backend-neutral helpers
(WMCreateDragOperationArray/Item, accessors, W_FreeViewXdndPart).

dragcommon_x11.c is new and owns:
  - W_OperationToAction/W_ActionToOperation atom translation
  - windowExists / handleNoWindowXError probe
  - W_SendDnDClientMessage XSendEvent path
  - W_HandleDNDClientMessage XDND wire dispatch
  - handleLeaveMessage

These touch scr->xdnd*Atom fields and call XSendEvent / XGetAtomName /
XSetErrorHandler etc. — strictly X11.  Wayland DnD will be added later
through wl_data_device.

dragsource.c and dragdestination.c still contain Xlib calls; those
splits come next.
This commit is contained in:
Window Maker 2026-05-24 10:58:22 +02:00
parent b5c46870aa
commit 9545320f9c
3 changed files with 230 additions and 212 deletions

View File

@ -28,6 +28,7 @@ EXTRA_DIST = BUGS make-rgb Examples Extras Tests
libWINGs_la_SOURCES = \
configuration.c \
dragcommon.c \
dragcommon_x11.c \
dragdestination.c \
dragsource.c \
rgb.h \

View File

@ -1,69 +1,14 @@
/*
* dragcommon.c — backend-neutral DnD helpers.
*
* X11-specific XDND code has moved to dragcommon_x11.c. This file
* holds only the parts that don't touch a windowing system: array
* accessors and free-on-view-destroy bookkeeping.
*/
#include "wconfig.h"
#include "WINGsP.h"
#define XDND_SOURCE_VERSION(dragInfo) dragInfo->protocolVersion
#define XDND_DEST_INFO(dragInfo) dragInfo->destInfo
#define XDND_DEST_VIEW(dragInfo) dragInfo->destInfo->destView
static Bool _WindowExists;
WMNativeAtom W_OperationToAction(WMScreen * scr, WMDragOperationType operation)
{
switch (operation) {
case WDOperationNone:
return WM_NATIVE_NONE;
case WDOperationCopy:
return scr->xdndActionCopy;
case WDOperationMove:
return scr->xdndActionMove;
case WDOperationLink:
return scr->xdndActionLink;
case WDOperationAsk:
return scr->xdndActionAsk;
case WDOperationPrivate:
return scr->xdndActionPrivate;
default:
return WM_NATIVE_NONE;
}
}
WMDragOperationType W_ActionToOperation(WMScreen * scr, WMNativeAtom action)
{
if (action == scr->xdndActionCopy) {
return WDOperationCopy;
} else if (action == scr->xdndActionMove) {
return WDOperationMove;
} else if (action == scr->xdndActionLink) {
return WDOperationLink;
} else if (action == scr->xdndActionAsk) {
return WDOperationAsk;
} else if (action == scr->xdndActionPrivate) {
return WDOperationPrivate;
} else if (action == WM_NATIVE_NONE) {
return WDOperationNone;
} else {
char *tmp = XGetAtomName(scr->display, action);
wwarning(_("unknown XDND action %s"), tmp);
XFree(tmp);
return WDOperationCopy;
}
}
static void freeDragOperationItem(void *item)
{
wfree(item);
@ -94,155 +39,6 @@ char *WMGetDragOperationItemText(WMDragOperationItem * item)
return ((W_DragOperationItem *) item)->text;
}
static int handleNoWindowXError(Display * dpy, XErrorEvent * errEvt)
{
/* Parameter not used, but tell the compiler that it is ok */
(void) dpy;
if (errEvt->error_code == BadWindow || errEvt->error_code == BadDrawable) {
_WindowExists = False;
return Success;
}
return errEvt->error_code;
}
static Bool windowExists(Display * dpy, WNativeWindow win)
{
void *previousErrorHandler;
XWindowAttributes attr;
XSynchronize(dpy, True);
previousErrorHandler = XSetErrorHandler(handleNoWindowXError);
_WindowExists = True;
/* can generate BadDrawable or BadWindow */
XGetWindowAttributes(dpy, win, &attr);
XSetErrorHandler(previousErrorHandler);
XSynchronize(dpy, False);
return _WindowExists;
}
Bool
W_SendDnDClientMessage(Display * dpy, WNativeWindow win, WMNativeAtom message,
unsigned long data0,
unsigned long data1, unsigned long data2, unsigned long data3, unsigned long data4)
{
XEvent ev;
#ifdef XDND_DEBUG
char *msgName = XGetAtomName(dpy, message);
printf("sending message %s ... ", msgName);
XFree(msgName);
#endif
if (!windowExists(dpy, win)) {
wwarning(_("target %lu for XDND message no longer exists"), win);
return False; /* message not sent */
}
ev.type = ClientMessage;
ev.xclient.message_type = message;
ev.xclient.format = 32;
ev.xclient.window = win;
ev.xclient.data.l[0] = data0;
ev.xclient.data.l[1] = data1;
ev.xclient.data.l[2] = data2;
ev.xclient.data.l[3] = data3;
ev.xclient.data.l[4] = data4;
XSendEvent(dpy, win, False, 0, &ev);
XFlush(dpy);
#ifdef XDND_DEBUG
printf("sent\n");
#endif
return True; /* message sent */
}
static void handleLeaveMessage(WMDraggingInfo * info)
{
if (XDND_DEST_INFO(info) != NULL) {
/* XDND_DEST_VIEW is never NULL (it's the xdnd aware view) */
wassertr(XDND_DEST_VIEW(info) != NULL);
if (XDND_DEST_VIEW(info)->dragDestinationProcs != NULL) {
XDND_DEST_VIEW(info)->dragDestinationProcs->concludeDragOperation(XDND_DEST_VIEW(info));
}
W_DragDestinationInfoClear(info);
}
}
void W_HandleDNDClientMessage(WMView * toplevel, XClientMessageEvent * event)
{
WMScreen *scr = W_VIEW_SCREEN(toplevel);
WMDraggingInfo *info = &scr->dragInfo;
WMNativeAtom messageType = event->message_type;
#ifdef XDND_DEBUG
{
char *msgTypeName = XGetAtomName(scr->display, messageType);
if (msgTypeName != NULL)
printf("event type = %s\n", msgTypeName);
else
printf("pb with event type !\n");
}
#endif
/* Messages from destination to source */
if (messageType == scr->xdndStatusAtom || messageType == scr->xdndFinishedAtom) {
W_DragSourceStopTimer();
W_DragSourceStateHandler(info, event);
return;
}
/* Messages from source to destination */
if (messageType == scr->xdndEnterAtom) {
Bool positionSent = (XDND_DEST_INFO(info) != NULL);
W_DragDestinationStopTimer();
W_DragDestinationStoreEnterMsgInfo(info, toplevel, event);
/* Xdnd version 3 and up are not compatible with version 1 or 2 */
if (XDND_SOURCE_VERSION(info) > 2) {
if (positionSent) {
/* xdndPosition previously received on xdnd aware view */
W_DragDestinationStateHandler(info, event);
return;
} else {
W_DragDestinationStartTimer(info);
return;
}
} else {
wwarning(_("unsupported version %i for XDND enter message"), XDND_SOURCE_VERSION(info));
W_DragDestinationCancelDropOnEnter(toplevel, info);
return;
}
}
if (messageType == scr->xdndPositionAtom) {
W_DragDestinationStopTimer();
W_DragDestinationStorePositionMsgInfo(info, toplevel, event);
W_DragDestinationStateHandler(info, event);
return;
}
if (messageType == scr->xdndSelectionAtom || messageType == scr->xdndDropAtom) {
W_DragDestinationStopTimer();
W_DragDestinationStateHandler(info, event);
return;
}
if (messageType == scr->xdndLeaveAtom) {
/* conclude drop operation, and clear dragging info */
W_DragDestinationStopTimer();
handleLeaveMessage(info);
}
}
/* called in destroyView (wview.c) */
void W_FreeViewXdndPart(WMView * view)
{

221
WINGs/dragcommon_x11.c Normal file
View File

@ -0,0 +1,221 @@
/*
* dragcommon_x11.c — X11 XDND-specific helpers.
*
* Operation↔atom translation, ClientMessage send/recv, error-handler
* window-exists probe — all things that talk Xlib/XDND wire protocol.
*/
#include "wconfig.h"
#include "WINGsP.h"
#include <X11/Xlib.h>
#define XDND_SOURCE_VERSION(dragInfo) dragInfo->protocolVersion
#define XDND_DEST_INFO(dragInfo) dragInfo->destInfo
#define XDND_DEST_VIEW(dragInfo) dragInfo->destInfo->destView
static Bool _WindowExists;
WMNativeAtom W_OperationToAction(WMScreen * scr, WMDragOperationType operation)
{
switch (operation) {
case WDOperationNone:
return WM_NATIVE_NONE;
case WDOperationCopy:
return scr->xdndActionCopy;
case WDOperationMove:
return scr->xdndActionMove;
case WDOperationLink:
return scr->xdndActionLink;
case WDOperationAsk:
return scr->xdndActionAsk;
case WDOperationPrivate:
return scr->xdndActionPrivate;
default:
return WM_NATIVE_NONE;
}
}
WMDragOperationType W_ActionToOperation(WMScreen * scr, WMNativeAtom action)
{
if (action == scr->xdndActionCopy) {
return WDOperationCopy;
} else if (action == scr->xdndActionMove) {
return WDOperationMove;
} else if (action == scr->xdndActionLink) {
return WDOperationLink;
} else if (action == scr->xdndActionAsk) {
return WDOperationAsk;
} else if (action == scr->xdndActionPrivate) {
return WDOperationPrivate;
} else if (action == WM_NATIVE_NONE) {
return WDOperationNone;
} else {
char *tmp = XGetAtomName(scr->display, action);
wwarning(_("unknown XDND action %s"), tmp);
XFree(tmp);
return WDOperationCopy;
}
}
static int handleNoWindowXError(Display * dpy, XErrorEvent * errEvt)
{
(void) dpy;
if (errEvt->error_code == BadWindow || errEvt->error_code == BadDrawable) {
_WindowExists = False;
return Success;
}
return errEvt->error_code;
}
static Bool windowExists(Display * dpy, WNativeWindow win)
{
void *previousErrorHandler;
XWindowAttributes attr;
XSynchronize(dpy, True);
previousErrorHandler = XSetErrorHandler(handleNoWindowXError);
_WindowExists = True;
/* can generate BadDrawable or BadWindow */
XGetWindowAttributes(dpy, win, &attr);
XSetErrorHandler(previousErrorHandler);
XSynchronize(dpy, False);
return _WindowExists;
}
Bool
W_SendDnDClientMessage(Display * dpy, WNativeWindow win, WMNativeAtom message,
unsigned long data0,
unsigned long data1, unsigned long data2, unsigned long data3, unsigned long data4)
{
XEvent ev;
#ifdef XDND_DEBUG
char *msgName = XGetAtomName(dpy, message);
printf("sending message %s ... ", msgName);
XFree(msgName);
#endif
if (!windowExists(dpy, win)) {
wwarning(_("target %lu for XDND message no longer exists"), win);
return False; /* message not sent */
}
ev.type = ClientMessage;
ev.xclient.message_type = message;
ev.xclient.format = 32;
ev.xclient.window = win;
ev.xclient.data.l[0] = data0;
ev.xclient.data.l[1] = data1;
ev.xclient.data.l[2] = data2;
ev.xclient.data.l[3] = data3;
ev.xclient.data.l[4] = data4;
XSendEvent(dpy, win, False, 0, &ev);
XFlush(dpy);
#ifdef XDND_DEBUG
printf("sent\n");
#endif
return True; /* message sent */
}
static void handleLeaveMessage(WMDraggingInfo * info)
{
if (XDND_DEST_INFO(info) != NULL) {
/* XDND_DEST_VIEW is never NULL (it's the xdnd aware view) */
wassertr(XDND_DEST_VIEW(info) != NULL);
if (XDND_DEST_VIEW(info)->dragDestinationProcs != NULL) {
XDND_DEST_VIEW(info)->dragDestinationProcs->concludeDragOperation(XDND_DEST_VIEW(info));
}
W_DragDestinationInfoClear(info);
}
}
void W_HandleDNDClientMessage(WMView * toplevel, XClientMessageEvent * event)
{
WMScreen *scr = W_VIEW_SCREEN(toplevel);
WMDraggingInfo *info = &scr->dragInfo;
WMNativeAtom messageType = event->message_type;
#ifdef XDND_DEBUG
{
char *msgTypeName = XGetAtomName(scr->display, messageType);
if (msgTypeName != NULL)
printf("event type = %s\n", msgTypeName);
else
printf("pb with event type !\n");
}
#endif
/* Messages from destination to source */
if (messageType == scr->xdndStatusAtom || messageType == scr->xdndFinishedAtom) {
W_DragSourceStopTimer();
W_DragSourceStateHandler(info, event);
return;
}
/* Messages from source to destination */
if (messageType == scr->xdndEnterAtom) {
Bool positionSent = (XDND_DEST_INFO(info) != NULL);
W_DragDestinationStopTimer();
W_DragDestinationStoreEnterMsgInfo(info, toplevel, event);
/* Xdnd version 3 and up are not compatible with version 1 or 2 */
if (XDND_SOURCE_VERSION(info) > 2) {
if (positionSent) {
/* xdndPosition previously received on xdnd aware view */
W_DragDestinationStateHandler(info, event);
return;
} else {
W_DragDestinationStartTimer(info);
return;
}
} else {
wwarning(_("unsupported version %i for XDND enter message"), XDND_SOURCE_VERSION(info));
W_DragDestinationCancelDropOnEnter(toplevel, info);
return;
}
}
if (messageType == scr->xdndPositionAtom) {
W_DragDestinationStopTimer();
W_DragDestinationStorePositionMsgInfo(info, toplevel, event);
W_DragDestinationStateHandler(info, event);
return;
}
if (messageType == scr->xdndSelectionAtom || messageType == scr->xdndDropAtom) {
W_DragDestinationStopTimer();
W_DragDestinationStateHandler(info, event);
return;
}
if (messageType == scr->xdndLeaveAtom) {
/* conclude drop operation, and clear dragging info */
W_DragDestinationStopTimer();
handleLeaveMessage(info);
}
}