#include "find_desktop.hh" #include "xcb_atoms.hh" #include "xcb_connection.hh" #include "xcb_event.hh" #include "xcb_resource.hh" #include #include #include #include #include #include #include #include #include #include namespace { int get_from_e17_using_window(xcb_connection_t* conn, xcb_window_t wnd, xcb_atom_t e_window_desk) { if (e_window_desk == XCB_ATOM_NONE) { return -1; } for (int i = 0; i < 2; ++i) { auto cookie = xcb_get_property(conn, 0, wnd, e_window_desk, XCB_ATOM_CARDINAL, 0, 2); xcb::reply reply( xcb_get_property_reply(conn, cookie, nullptr)); if (reply && reply->format == 32 && xcb_get_property_value_length(reply.get()) >= 8) { auto* data = static_cast(xcb_get_property_value(reply.get())); auto x = data[0]; auto y = data[1]; return static_cast((x * 256) + y); } xcb_flush(conn); struct timespec ts = {.tv_sec = 0, .tv_nsec = 250000000}; // 250ms nanosleep(&ts, nullptr); } return -1; } std::string create_desktop(std::string const& prefix, int desktop, bool use_prefix) { std::string ret; if (use_prefix) { ret.append(prefix); ret.push_back('_'); } auto const offset = ret.size(); ret.resize(offset + 10); auto [ptr, ec] = std::to_chars(ret.data() + offset, ret.data() + ret.size(), desktop); if (ec == std::errc()) { ret.resize(ptr - ret.data()); } else { assert(false); ret.resize(offset); } return ret; } } // namespace std::optional find_desktop(std::optional display, bool use_prefix) { int screen_num = 0; auto conn = xcb::make_shared_conn(xcb_connect( display.has_value() ? display.value().c_str() : nullptr, &screen_num)); if (!conn || xcb_connection_has_error(conn.get())) { return std::nullopt; } auto atoms = xcb::Atoms::create(conn); if (!atoms) { return std::nullopt; } auto* screen = xcb::get_screen(conn.get(), screen_num); if (!screen) { return std::nullopt; } xcb_window_t root = screen->root; auto net_current_desktop_atom = atoms->get("_NET_CURRENT_DESKTOP"); auto e_window_desk_atom = atoms->get("__E_WINDOW_DESK"); if (!atoms->sync()) { return std::nullopt; } auto* desktop_env = getenv("DESKTOP"); if (!desktop_env || strncmp(desktop_env, "Enlightenment-0.17", 18) != 0) { /* Skip _NET_CURRENT_DESKTOP if we're in e17 */ /* Try to read the _NET_CURRENT_DESKTOP property */ auto net_current_desktop = net_current_desktop_atom.get(); if (net_current_desktop != XCB_ATOM_NONE) { auto cookie = xcb_get_property(conn.get(), 0, root, net_current_desktop, XCB_ATOM_CARDINAL, 0, 1); xcb::reply reply( xcb_get_property_reply(conn.get(), cookie, nullptr)); if (reply && reply->format == 32 && xcb_get_property_value_length(reply.get()) >= 4) { auto* data = static_cast(xcb_get_property_value(reply.get())); return create_desktop("ewmh", static_cast(data[0]), use_prefix); } } } /* Failing that, let's see if our owner knows */ auto* windowid_env = getenv("WINDOWID"); if (windowid_env) { auto* const end = windowid_env + strlen(windowid_env); unsigned long windowid; auto [ptr, ec] = std::from_chars(windowid_env, end, windowid); if (ptr == end && ec == std::errc{}) { auto ret = get_from_e17_using_window(conn.get(), static_cast(windowid), e_window_desk_atom.get()); if (ret != -1) { return create_desktop("enlightenment", ret, use_prefix); } } } /* Try creating a bogus window then */ { auto wnd = xcb::make_unique_wnd(conn); xcb_create_window(conn.get(), XCB_COPY_FROM_PARENT, wnd->id(), root, 0, 0, 1, 1, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, 0, nullptr); xcb_map_window(conn.get(), wnd->id()); xcb_flush(conn.get()); auto ret = get_from_e17_using_window(conn.get(), wnd->id(), e_window_desk_atom.get()); if (ret != -1) { return create_desktop("enlightenment", ret, use_prefix); } } /* Don't know what else to do */ return std::nullopt; }