You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Linux下拖拽复制/移动文件时如何通过X11接口获取文件名?

用X11接口实现拖拽文件时获取文件名

可以通过X11的Xdnd(X Drag and Drop)协议实现该功能。Xdnd是X11环境下标准的拖放交互协议,支持窗口间传递各类数据,包括文件路径信息。

核心原理

拖放过程中,源窗口会将文件路径以text/uri-list格式的数据通过Xdnd协议传递给目标窗口。我们只需在目标窗口中监听Xdnd相关事件,解析传递的URI列表即可提取文件名。

示例代码

以下是一个C语言实现的简单示例,创建一个X11窗口作为拖放目标,当文件拖拽至该窗口时,解析并打印文件名:

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// 定义Xdnd相关原子
Atom XdndEnter, XdndLeave, XdndDrop, XdndPosition;
Atom XdndActionCopy, XdndActionMove, XdndActionAsk;
Atom XdndStatus, XdndTypeList, XdndSelection;
Atom text_uri_list, utf8_string;

Window create_window(Display *dpy, int width, int height) {
    Window root = DefaultRootWindow(dpy);
    XSetWindowAttributes attr;
    attr.event_mask = ExposureMask | ButtonPressMask | StructureNotifyMask;
    attr.background_pixel = WhitePixel(dpy, DefaultScreen(dpy));
    
    Window win = XCreateWindow(dpy, root, 0, 0, width, height, 0,
                               DefaultDepth(dpy, DefaultScreen(dpy)),
                               InputOutput, DefaultVisual(dpy, DefaultScreen(dpy)),
                               CWBackPixel | CWEventMask, &attr);
    
    XStoreName(dpy, win, "X11 Drop Target");
    XMapWindow(dpy, win);
    return win;
}

// 解析URI列表,提取文件名
void parse_uri_list(char *data) {
    char *line = strtok(data, "\r\n");
    while (line != NULL) {
        // 处理file://格式的URI,去除前缀
        if (strncmp(line, "file://", 7) == 0) {
            char *path = line + 7;
            // 简单处理URI转义字符(完整实现需遵循RFC 3986)
            for (char *p = path; *p; p++) {
                if (*p == '%' && *(p+1) && *(p+2)) {
                    char hex[3] = {*(p+1), *(p+2), '\0'};
                    *p = strtol(hex, NULL, 16);
                    memmove(p+1, p+3, strlen(p+3)+1);
                }
            }
            // 从路径中提取文件名
            char *filename = strrchr(path, '/');
            if (filename) {
                printf("拖拽的文件名:%s\n", filename + 1);
            } else {
                printf("拖拽的文件名:%s\n", path);
            }
        }
        line = strtok(NULL, "\r\n");
    }
}

// 处理Xdnd事件
void handle_xdnd_event(Display *dpy, XEvent *ev) {
    switch (ev->type) {
        case ClientMessage: {
            XClientMessageEvent *cme = &ev->xclient;
            if (cme->message_type == XdndEnter) {
                // 响应XdndEnter,告知窗口支持拖放
                XClientMessageEvent reply;
                reply.type = ClientMessage;
                reply.window = cme->data.l[0];
                reply.message_type = XdndStatus;
                reply.format = 32;
                reply.data.l[0] = ev->xclient.window;
                reply.data.l[1] = 1; // 接受拖放
                reply.data.l[2] = XdndActionCopy | XdndActionMove; // 支持的操作类型
                reply.data.l[3] = 0;
                reply.data.l[4] = 0;
                XSendEvent(dpy, cme->data.l[0], False, NoEventMask, (XEvent*)&reply);
                XFlush(dpy);
            } else if (cme->message_type == XdndDrop) {
                // 请求获取拖放数据
                XConvertSelection(dpy, XdndSelection, text_uri_list, XdndSelection, ev->xclient.window, CurrentTime);
                XFlush(dpy);
            }
            break;
        }
        case SelectionNotify: {
            XSelectionEvent *se = &ev->xselection;
            if (se->selection == XdndSelection && se->target == text_uri_list && se->property != None) {
                Atom actual_type;
                int format;
                unsigned long nitems, bytes_after;
                unsigned char *data = NULL;
                // 获取拖放的URI数据
                if (XGetWindowProperty(dpy, se->requestor, se->property, 0, 1024*1024, False,
                                       AnyPropertyType, &actual_type, &format, &nitems, &bytes_after, &data) == Success && data) {
                    parse_uri_list((char*)data);
                    XFree(data);
                }
                // 删除临时属性
                XDeleteProperty(dpy, se->requestor, se->property);
            }
            break;
        }
    }
}

int main() {
    Display *dpy = XOpenDisplay(NULL);
    if (!dpy) {
        fprintf(stderr, "无法打开X11显示\n");
        return 1;
    }
    int screen = DefaultScreen(dpy);
    Window win = create_window(dpy, 400, 300);
    
    // 初始化所需的X11原子
    XdndEnter = XInternAtom(dpy, "XdndEnter", False);
    XdndLeave = XInternAtom(dpy, "XdndLeave", False);
    XdndDrop = XInternAtom(dpy, "XdndDrop", False);
    XdndPosition = XInternAtom(dpy, "XdndPosition", False);
    XdndActionCopy = XInternAtom(dpy, "XdndActionCopy", False);
    XdndActionMove = XInternAtom(dpy, "XdndActionMove", False);
    XdndActionAsk = XInternAtom(dpy, "XdndActionAsk", False);
    XdndStatus = XInternAtom(dpy, "XdndStatus", False);
    XdndTypeList = XInternAtom(dpy, "XdndTypeList", False);
    XdndSelection = XInternAtom(dpy, "XdndSelection", False);
    text_uri_list = XInternAtom(dpy, "text/uri-list", False);
    utf8_string = XInternAtom(dpy, "UTF8_STRING", False);
    
    // 设置窗口支持的拖放数据类型
    Atom supported_types[] = {text_uri_list, utf8_string};
    XChangeProperty(dpy, win, XdndTypeList, XA_ATOM, 32, PropModeReplace,
                   (unsigned char*)supported_types, sizeof(supported_types)/sizeof(Atom));
    
    XEvent ev;
    while (1) {
        XNextEvent(dpy, &ev);
        if (ev.type == ClientMessage || ev.type == SelectionNotify) {
            handle_xdnd_event(dpy, &ev);
        } else if (ev.type == DestroyNotify) {
            break;
        }
    }
    
    XCloseDisplay(dpy);
    return 0;
}

编译与运行

需先安装X11开发库(如libx11-dev),然后用以下命令编译:

gcc -o x11_drop_target x11_drop_target.c -lX11

运行编译后的程序,将文件拖拽到弹出的白色窗口中,终端会打印出对应的文件名。

注意事项

  • 示例仅处理了file://格式的URI,实际场景可扩展支持更多格式。
  • URI转义字符处理做了简化,完整实现需遵循RFC 3986标准。
  • 若需处理文件移动操作(而非复制),需额外与源窗口交互确认操作结果。

内容的提问来源于stack exchange,提问作者zhiming wen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 19:12:37