diff options
| author | Joel Klinghed <the_jk@opera.com> | 2026-01-12 23:06:20 +0100 |
|---|---|---|
| committer | Joel Klinghed <the_jk@opera.com> | 2026-01-12 23:06:20 +0100 |
| commit | dfeb19b0a83b8ce57d28bf94a4f8d129993d1064 (patch) | |
| tree | d352908df286058059e306c350d89a07c67049eb /src/find_desktop.cc | |
Initial commit
Diffstat (limited to 'src/find_desktop.cc')
| -rw-r--r-- | src/find_desktop.cc | 152 |
1 files changed, 152 insertions, 0 deletions
diff --git a/src/find_desktop.cc b/src/find_desktop.cc new file mode 100644 index 0000000..a3a9330 --- /dev/null +++ b/src/find_desktop.cc @@ -0,0 +1,152 @@ +#include "find_desktop.hh" + +#include "xcb_atoms.hh" +#include "xcb_connection.hh" +#include "xcb_event.hh" +#include "xcb_resource.hh" + +#include <cassert> +#include <charconv> +#include <cstdint> +#include <cstdlib> +#include <cstring> +#include <ctime> +#include <optional> +#include <string> +#include <system_error> +#include <xcb/xproto.h> + +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<xcb_get_property_reply_t> 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<uint32_t*>(xcb_get_property_value(reply.get())); + auto x = data[0]; + auto y = data[1]; + return static_cast<int>((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<std::string> find_desktop(std::optional<std::string> 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<xcb_get_property_reply_t> 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<uint32_t*>(xcb_get_property_value(reply.get())); + return create_desktop("ewmh", static_cast<int>(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<xcb_window_t>(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; +} |
