Hyprland/src/xwayland/XWM.hpp
Zynix c92fb5e85f
xwayland/xwm: prevent onWrite infinite loop and clean orphan transfers (#13122)
Fixes #11411

- Add return 0 after erasing completed non-incremental transfer to stop event source polling
- Add removeTransfer() helper to SXSelection for cleaning transfers by window ID
- Add removeTransfersForWindow() helper to CXWM for cleaning all selections at once
- Clean orphan transfers in handleDestroy before surface removal
- Clean orphan transfers in handlePropertyNotify on missing window or failed reply
- Add m_dndSelection to handleSelectionPropertyNotify cleanup loop
- Initialize SXTransfer members with safe defaults to prevent undefined behavior
- Fix race condition in getTransferData by using window ID lookup instead of index
2026-01-29 13:50:17 +00:00

236 lines
8.4 KiB
C++

#pragma once
#include "XDataSource.hpp"
#include "Dnd.hpp"
#include "../helpers/memory/Memory.hpp"
#include "../helpers/signal/Signal.hpp"
#include <xcb/xcb.h>
#include <xcb/res.h>
#include <xcb/xfixes.h>
#include <xcb/composite.h>
#include <xcb/xcb_errors.h>
#include <hyprutils/os/FileDescriptor.hpp>
#include <cinttypes> // for PRIxPTR
#include <cstdint>
struct wl_event_source;
class CXWaylandSurfaceResource;
struct SXSelection;
struct SXTransfer {
~SXTransfer();
SXSelection& selection;
bool out = true;
bool incremental = false;
bool flushOnDelete = false;
bool propertySet = false;
Hyprutils::OS::CFileDescriptor wlFD;
wl_event_source* eventSource = nullptr;
std::vector<uint8_t> data;
xcb_selection_request_event_t request;
int propertyStart = 0;
xcb_get_property_reply_t* propertyReply = nullptr;
xcb_window_t incomingWindow = 0;
bool getIncomingSelectionProp(bool erase);
};
struct SXSelection {
xcb_window_t window = 0;
xcb_window_t owner = 0;
xcb_timestamp_t timestamp = 0;
SP<CXDataSource> dataSource;
bool notifyOnFocus = false;
void onSelection();
void onKeyboardFocus();
bool sendData(xcb_selection_request_event_t* e, std::string mime);
int onRead(int fd, uint32_t mask);
int onWrite();
void removeTransfer(xcb_window_t window);
struct {
CHyprSignalListener setSelection;
CHyprSignalListener keyboardFocusChange;
} listeners;
std::vector<UP<SXTransfer>> transfers;
};
class CXCBConnection {
public:
CXCBConnection(int fd) : m_connection{xcb_connect_to_fd(fd, nullptr)} {
;
}
~CXCBConnection() {
if (m_connection) {
Log::logger->log(Log::DEBUG, "Disconnecting XCB connection {:x}", rc<uintptr_t>(m_connection));
xcb_disconnect(m_connection);
m_connection = nullptr;
} else
Log::logger->log(Log::ERR, "Double xcb_disconnect attempt");
}
bool hasError() const {
return xcb_connection_has_error(m_connection);
}
operator xcb_connection_t*() const {
return m_connection;
}
private:
xcb_connection_t* m_connection = nullptr;
};
class CXCBErrorContext {
public:
explicit CXCBErrorContext(xcb_connection_t* connection) {
if (xcb_errors_context_new(connection, &m_errors) != 0)
m_errors = nullptr;
}
~CXCBErrorContext() {
if (m_errors)
xcb_errors_context_free(m_errors);
}
bool isValid() const {
return m_errors != nullptr;
}
private:
xcb_errors_context_t* m_errors = nullptr;
};
class CXWM {
public:
CXWM();
~CXWM();
int onEvent(int fd, uint32_t mask);
SP<CX11DataDevice> getDataDevice();
SP<IDataOffer> createX11DataOffer(SP<CWLSurfaceResource> surf, SP<IDataSource> source);
void updateWorkArea(int x, int y, int w, int h);
private:
void setCursor(unsigned char* pixData, uint32_t stride, const Vector2D& size, const Vector2D& hotspot);
void gatherResources();
void getVisual();
void getRenderFormat();
void createWMWindow();
void initSelection();
void onNewSurface(SP<CWLSurfaceResource> surf);
void onNewResource(SP<CXWaylandSurfaceResource> resource);
void setActiveWindow(xcb_window_t window);
void sendState(SP<CXWaylandSurface> surf);
void focusWindow(SP<CXWaylandSurface> surf);
void activateSurface(SP<CXWaylandSurface> surf, bool activate);
bool isWMWindow(xcb_window_t w);
void updateOverrideRedirect(SP<CXWaylandSurface> surf, bool overrideRedirect);
void sendWMMessage(SP<CXWaylandSurface> surf, xcb_client_message_data_t* data, uint32_t mask);
SP<CXWaylandSurface> windowForXID(xcb_window_t wid);
SP<CXWaylandSurface> windowForWayland(SP<CWLSurfaceResource> surf);
void readWindowData(SP<CXWaylandSurface> surf);
void associate(SP<CXWaylandSurface> surf, SP<CWLSurfaceResource> wlSurf);
void dissociate(SP<CXWaylandSurface> surf);
void updateClientList();
// event handlers
void handleCreate(xcb_create_notify_event_t* e);
void handleDestroy(xcb_destroy_notify_event_t* e);
void handleConfigureRequest(xcb_configure_request_event_t* e);
void handleConfigureNotify(xcb_configure_notify_event_t* e);
void handleMapRequest(xcb_map_request_event_t* e);
void handleMapNotify(xcb_map_notify_event_t* e);
void handleUnmapNotify(xcb_unmap_notify_event_t* e);
void handlePropertyNotify(xcb_property_notify_event_t* e);
void handleClientMessage(xcb_client_message_event_t* e);
void handleFocusIn(xcb_focus_in_event_t* e);
void handleFocusOut(xcb_focus_out_event_t* e);
void handleError(xcb_value_error_t* e);
void removeTransfersForWindow(xcb_window_t window);
bool handleSelectionEvent(xcb_generic_event_t* e);
void handleSelectionNotify(xcb_selection_notify_event_t* e);
bool handleSelectionPropertyNotify(xcb_property_notify_event_t* e);
void handleSelectionRequest(xcb_selection_request_event_t* e);
bool handleSelectionXFixesNotify(xcb_xfixes_selection_notify_event_t* e);
void selectionSendNotify(xcb_selection_request_event_t* e, bool success);
xcb_atom_t mimeToAtom(const std::string& mime);
std::string mimeFromAtom(xcb_atom_t atom);
void setClipboardToWayland(SXSelection& sel);
void getTransferData(SXSelection& sel);
std::string getAtomName(uint32_t atom);
void readProp(SP<CXWaylandSurface> XSURF, uint32_t atom, xcb_get_property_reply_t* reply);
SXSelection* getSelection(xcb_atom_t atom);
//
UP<CXCBConnection> m_connection;
xcb_errors_context_t* m_errors = nullptr;
xcb_screen_t* m_screen = nullptr;
xcb_window_t m_wmWindow;
wl_event_source* m_eventSource = nullptr;
const xcb_query_extension_reply_t* m_xfixes = nullptr;
const xcb_query_extension_reply_t* m_xres = nullptr;
int m_xfixesMajor = 0;
xcb_visualid_t m_visualID;
xcb_colormap_t m_colormap;
uint32_t m_cursorXID = 0;
xcb_render_pictformat_t m_renderFormatID;
std::vector<WP<CXWaylandSurfaceResource>> m_shellResources;
std::vector<SP<CXWaylandSurface>> m_surfaces;
std::vector<WP<CXWaylandSurface>> m_mappedSurfaces; // ordered by map time
std::vector<WP<CXWaylandSurface>> m_mappedSurfacesStacking; // ordered by stacking
WP<CXWaylandSurface> m_focusedSurface;
uint64_t m_lastFocusSeq = 0;
SXSelection m_clipboard;
SXSelection m_primarySelection;
SXSelection m_dndSelection;
SP<CX11DataDevice> m_dndDataDevice = makeShared<CX11DataDevice>();
std::vector<SP<CX11DataOffer>> m_dndDataOffers;
inline xcb_connection_t* getConnection() {
return m_connection ? *m_connection : nullptr;
}
struct {
CHyprSignalListener newWLSurface;
CHyprSignalListener newXShellSurface;
} m_listeners;
friend class CXWaylandSurface;
friend class CXWayland;
friend class CXDataSource;
friend class CX11DataDevice;
friend class CX11DataSource;
friend class CX11DataOffer;
friend class CWLDataDeviceProtocol;
friend struct SXSelection;
friend struct SXTransfer;
};