1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
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;
}
|