xwayland: sync primary selection with wayland (#9952)

This commit is contained in:
nyx
2025-04-08 11:36:29 -04:00
committed by GitHub
parent b15c2bfff6
commit 642f394eb3
3 changed files with 62 additions and 18 deletions

View File

@@ -77,7 +77,13 @@ void CXDataSource::send(const std::string& mime, CFileDescriptor fd) {
xcb_create_window(g_pXWayland->pWM->connection, XCB_COPY_FROM_PARENT, transfer->incomingWindow, g_pXWayland->pWM->screen->root, 0, 0, 10, 10, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, xcb_create_window(g_pXWayland->pWM->connection, XCB_COPY_FROM_PARENT, transfer->incomingWindow, g_pXWayland->pWM->screen->root, 0, 0, 10, 10, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT,
g_pXWayland->pWM->screen->root_visual, XCB_CW_EVENT_MASK, &MASK); g_pXWayland->pWM->screen->root_visual, XCB_CW_EVENT_MASK, &MASK);
xcb_convert_selection(g_pXWayland->pWM->connection, transfer->incomingWindow, HYPRATOMS["CLIPBOARD"], mimeAtom, HYPRATOMS["_WL_SELECTION"], XCB_TIME_CURRENT_TIME); xcb_atom_t selection_atom = HYPRATOMS["CLIPBOARD"];
if (&selection == &g_pXWayland->pWM->primarySelection)
selection_atom = HYPRATOMS["PRIMARY"];
else if (&selection == &g_pXWayland->pWM->dndSelection)
selection_atom = HYPRATOMS["XdndSelection"];
xcb_convert_selection(g_pXWayland->pWM->connection, transfer->incomingWindow, selection_atom, mimeAtom, HYPRATOMS["_WL_SELECTION"], XCB_TIME_CURRENT_TIME);
xcb_flush(g_pXWayland->pWM->connection); xcb_flush(g_pXWayland->pWM->connection);

View File

@@ -586,7 +586,7 @@ void CXWM::handleSelectionNotify(xcb_selection_notify_event_t* e) {
Debug::log(TRACE, "[xwm] converting selection failed"); Debug::log(TRACE, "[xwm] converting selection failed");
sel->transfers.erase(it); sel->transfers.erase(it);
} }
} else if (e->target == HYPRATOMS["TARGETS"] && sel == &clipboard) { } else if (e->target == HYPRATOMS["TARGETS"]) {
if (!focusedSurface) { if (!focusedSurface) {
Debug::log(TRACE, "[xwm] denying access to write to clipboard because no X client is in focus"); Debug::log(TRACE, "[xwm] denying access to write to clipboard because no X client is in focus");
return; return;
@@ -601,14 +601,16 @@ bool CXWM::handleSelectionPropertyNotify(xcb_property_notify_event_t* e) {
if (e->state != XCB_PROPERTY_DELETE) if (e->state != XCB_PROPERTY_DELETE)
return false; return false;
auto it = std::ranges::find_if(clipboard.transfers, [e](const auto& t) { return t->incomingWindow == e->window; }); for (auto* sel : {&clipboard, &primarySelection}) {
if (it != clipboard.transfers.end()) { auto it = std::ranges::find_if(sel->transfers, [e](const auto& t) { return t->incomingWindow == e->window; });
if (!(*it)->getIncomingSelectionProp(true)) { if (it != sel->transfers.end()) {
clipboard.transfers.erase(it); if (!(*it)->getIncomingSelectionProp(true)) {
return false; sel->transfers.erase(it);
return false;
}
getTransferData(*sel);
return true;
} }
getTransferData(clipboard);
return true;
} }
return false; return false;
@@ -617,6 +619,8 @@ bool CXWM::handleSelectionPropertyNotify(xcb_property_notify_event_t* e) {
SXSelection* CXWM::getSelection(xcb_atom_t atom) { SXSelection* CXWM::getSelection(xcb_atom_t atom) {
if (atom == HYPRATOMS["CLIPBOARD"]) if (atom == HYPRATOMS["CLIPBOARD"])
return &clipboard; return &clipboard;
else if (atom == HYPRATOMS["PRIMARY"])
return &primarySelection;
else if (atom == HYPRATOMS["XdndSelection"]) else if (atom == HYPRATOMS["XdndSelection"])
return &dndSelection; return &dndSelection;
@@ -707,8 +711,12 @@ bool CXWM::handleSelectionXFixesNotify(xcb_xfixes_selection_notify_event_t* e) {
return true; return true;
if (e->owner == XCB_WINDOW_NONE) { if (e->owner == XCB_WINDOW_NONE) {
if (sel->owner != sel->window && sel == &clipboard) if (sel->owner != sel->window) {
g_pSeatManager->setCurrentSelection(nullptr); if (sel == &clipboard)
g_pSeatManager->setCurrentSelection(nullptr);
else if (sel == &primarySelection)
g_pSeatManager->setCurrentPrimarySelection(nullptr);
}
sel->owner = 0; sel->owner = 0;
return true; return true;
@@ -721,7 +729,10 @@ bool CXWM::handleSelectionXFixesNotify(xcb_xfixes_selection_notify_event_t* e) {
return true; return true;
} }
xcb_convert_selection(connection, sel->window, HYPRATOMS["CLIPBOARD"], HYPRATOMS["TARGETS"], HYPRATOMS["_WL_SELECTION"], e->timestamp); if (sel == &clipboard)
xcb_convert_selection(connection, sel->window, HYPRATOMS["CLIPBOARD"], HYPRATOMS["TARGETS"], HYPRATOMS["_WL_SELECTION"], e->timestamp);
else if (sel == &primarySelection)
xcb_convert_selection(connection, sel->window, HYPRATOMS["PRIMARY"], HYPRATOMS["TARGETS"], HYPRATOMS["_WL_SELECTION"], e->timestamp);
xcb_flush(connection); xcb_flush(connection);
return true; return true;
@@ -1171,6 +1182,16 @@ void CXWM::initSelection() {
clipboard.listeners.setSelection = g_pSeatManager->events.setSelection.registerListener([this](std::any d) { clipboard.onSelection(); }); clipboard.listeners.setSelection = g_pSeatManager->events.setSelection.registerListener([this](std::any d) { clipboard.onSelection(); });
clipboard.listeners.keyboardFocusChange = g_pSeatManager->events.keyboardFocusChange.registerListener([this](std::any d) { clipboard.onKeyboardFocus(); }); clipboard.listeners.keyboardFocusChange = g_pSeatManager->events.keyboardFocusChange.registerListener([this](std::any d) { clipboard.onKeyboardFocus(); });
primarySelection.window = xcb_generate_id(connection);
xcb_create_window(connection, XCB_COPY_FROM_PARENT, primarySelection.window, screen->root, 0, 0, 10, 10, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, screen->root_visual,
XCB_CW_EVENT_MASK, mask);
xcb_set_selection_owner(connection, primarySelection.window, HYPRATOMS["PRIMARY"], XCB_TIME_CURRENT_TIME);
xcb_xfixes_select_selection_input(connection, primarySelection.window, HYPRATOMS["PRIMARY"], mask2);
primarySelection.listeners.setSelection = g_pSeatManager->events.setPrimarySelection.registerListener([this](std::any d) { primarySelection.onSelection(); });
primarySelection.listeners.keyboardFocusChange = g_pSeatManager->events.keyboardFocusChange.registerListener([this](std::any d) { primarySelection.onKeyboardFocus(); });
dndSelection.window = xcb_generate_id(connection); dndSelection.window = xcb_generate_id(connection);
xcb_create_window(connection, XCB_COPY_FROM_PARENT, dndSelection.window, screen->root, 0, 0, 8192, 8192, 0, XCB_WINDOW_CLASS_INPUT_ONLY, screen->root_visual, XCB_CW_EVENT_MASK, xcb_create_window(connection, XCB_COPY_FROM_PARENT, dndSelection.window, screen->root, 0, 0, 8192, 8192, 0, XCB_WINDOW_CLASS_INPUT_ONLY, screen->root_visual, XCB_CW_EVENT_MASK,
mask); mask);
@@ -1182,14 +1203,18 @@ void CXWM::initSelection() {
void CXWM::setClipboardToWayland(SXSelection& sel) { void CXWM::setClipboardToWayland(SXSelection& sel) {
auto source = makeShared<CXDataSource>(sel); auto source = makeShared<CXDataSource>(sel);
if (source->mimes().empty()) { if (source->mimes().empty()) {
Debug::log(ERR, "[xwm] can't set clipboard: no MIMEs"); Debug::log(ERR, "[xwm] can't set selection: no MIMEs");
return; return;
} }
sel.dataSource = source; sel.dataSource = source;
Debug::log(LOG, "[xwm] X clipboard at {:x} takes clipboard", (uintptr_t)sel.dataSource.get()); Debug::log(LOG, "[xwm] X selection at {:x} takes {}", (uintptr_t)sel.dataSource.get(), (&sel == &clipboard) ? "clipboard" : "primary selection");
g_pSeatManager->setCurrentSelection(sel.dataSource);
if (&sel == &clipboard)
g_pSeatManager->setCurrentSelection(sel.dataSource);
else if (&sel == &primarySelection)
g_pSeatManager->setCurrentPrimarySelection(sel.dataSource);
} }
static int writeDataSource(int fd, uint32_t mask, void* data) { static int writeDataSource(int fd, uint32_t mask, void* data) {
@@ -1305,22 +1330,32 @@ SP<IDataOffer> CXWM::createX11DataOffer(SP<CWLSurfaceResource> surf, SP<IDataSou
} }
void SXSelection::onSelection() { void SXSelection::onSelection() {
if (g_pSeatManager->selection.currentSelection && g_pSeatManager->selection.currentSelection->type() == DATA_SOURCE_TYPE_X11) if ((this == &g_pXWayland->pWM->clipboard && g_pSeatManager->selection.currentSelection && g_pSeatManager->selection.currentSelection->type() == DATA_SOURCE_TYPE_X11) ||
(this == &g_pXWayland->pWM->primarySelection && g_pSeatManager->selection.currentPrimarySelection &&
g_pSeatManager->selection.currentPrimarySelection->type() == DATA_SOURCE_TYPE_X11))
return; return;
if (g_pSeatManager->selection.currentSelection) { if (this == &g_pXWayland->pWM->clipboard && g_pSeatManager->selection.currentSelection) {
xcb_set_selection_owner(g_pXWayland->pWM->connection, g_pXWayland->pWM->clipboard.window, HYPRATOMS["CLIPBOARD"], XCB_TIME_CURRENT_TIME); xcb_set_selection_owner(g_pXWayland->pWM->connection, g_pXWayland->pWM->clipboard.window, HYPRATOMS["CLIPBOARD"], XCB_TIME_CURRENT_TIME);
xcb_flush(g_pXWayland->pWM->connection); xcb_flush(g_pXWayland->pWM->connection);
g_pXWayland->pWM->clipboard.notifyOnFocus = true; g_pXWayland->pWM->clipboard.notifyOnFocus = true;
} else if (this == &g_pXWayland->pWM->primarySelection && g_pSeatManager->selection.currentPrimarySelection) {
xcb_set_selection_owner(g_pXWayland->pWM->connection, g_pXWayland->pWM->primarySelection.window, HYPRATOMS["PRIMARY"], XCB_TIME_CURRENT_TIME);
xcb_flush(g_pXWayland->pWM->connection);
g_pXWayland->pWM->primarySelection.notifyOnFocus = true;
} }
} }
void SXSelection::onKeyboardFocus() { void SXSelection::onKeyboardFocus() {
if (!g_pSeatManager->state.keyboardFocusResource || g_pSeatManager->state.keyboardFocusResource->client() != g_pXWayland->pServer->xwaylandClient) if (!g_pSeatManager->state.keyboardFocusResource || g_pSeatManager->state.keyboardFocusResource->client() != g_pXWayland->pServer->xwaylandClient)
return; return;
if (g_pXWayland->pWM->clipboard.notifyOnFocus) {
if (this == &g_pXWayland->pWM->clipboard && g_pXWayland->pWM->clipboard.notifyOnFocus) {
onSelection(); onSelection();
g_pXWayland->pWM->clipboard.notifyOnFocus = false; g_pXWayland->pWM->clipboard.notifyOnFocus = false;
} else if (this == &g_pXWayland->pWM->primarySelection && g_pXWayland->pWM->primarySelection.notifyOnFocus) {
onSelection();
g_pXWayland->pWM->primarySelection.notifyOnFocus = false;
} }
} }
@@ -1370,6 +1405,8 @@ bool SXSelection::sendData(xcb_selection_request_event_t* e, std::string mime) {
WP<IDataSource> selection; WP<IDataSource> selection;
if (this == &g_pXWayland->pWM->clipboard) if (this == &g_pXWayland->pWM->clipboard)
selection = g_pSeatManager->selection.currentSelection; selection = g_pSeatManager->selection.currentSelection;
else if (this == &g_pXWayland->pWM->primarySelection)
selection = g_pSeatManager->selection.currentPrimarySelection;
else if (!g_pXWayland->pWM->dndDataOffers.empty()) else if (!g_pXWayland->pWM->dndDataOffers.empty())
selection = g_pXWayland->pWM->dndDataOffers.at(0)->getSource(); selection = g_pXWayland->pWM->dndDataOffers.at(0)->getSource();

View File

@@ -201,6 +201,7 @@ class CXWM {
uint64_t lastFocusSeq = 0; uint64_t lastFocusSeq = 0;
SXSelection clipboard; SXSelection clipboard;
SXSelection primarySelection;
SXSelection dndSelection; SXSelection dndSelection;
SP<CX11DataDevice> dndDataDevice = makeShared<CX11DataDevice>(); SP<CX11DataDevice> dndDataDevice = makeShared<CX11DataDevice>();
std::vector<SP<CX11DataOffer>> dndDataOffers; std::vector<SP<CX11DataOffer>> dndDataOffers;