summaryrefslogtreecommitdiff
path: root/src/find_desktop.cc
diff options
context:
space:
mode:
authorJoel Klinghed <the_jk@opera.com>2026-01-12 23:06:20 +0100
committerJoel Klinghed <the_jk@opera.com>2026-01-12 23:06:20 +0100
commitdfeb19b0a83b8ce57d28bf94a4f8d129993d1064 (patch)
treed352908df286058059e306c350d89a07c67049eb /src/find_desktop.cc
Initial commit
Diffstat (limited to 'src/find_desktop.cc')
-rw-r--r--src/find_desktop.cc152
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;
+}