/* * Copyright 2026 Joel Klinghed * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #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; }