-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.cc
157 lines (140 loc) · 5 KB
/
main.cc
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
153
154
155
156
157
#include "widget.h"
#include "xcb.h"
#include <algorithm>
namespace {
class WidgetView {
private:
int width_;
int height_;
const uint8_t* state_;
xcb_window_t window_;
xcb_gcontext_t context_;
static xcb_window_t CreateWindow(xcb_connection_t* conn, int width, int height) {
auto result = xcb_generate_id(conn);
auto screen = xcb_setup_roots_iterator(xcb_get_setup(conn)).data;
uint32_t mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;
uint32_t values[] = {screen->white_pixel, XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_RESIZE_REDIRECT};
xcb_create_window(conn, XCB_COPY_FROM_PARENT, result, screen->root, 0, 0, width, height, 0,
XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, mask, values);
return result;
}
static xcb_gcontext_t CreateContext(xcb_connection_t* conn) {
auto result = xcb_generate_id(conn);
auto screen = xcb_setup_roots_iterator(xcb_get_setup(conn)).data;
uint32_t mask = XCB_GC_FOREGROUND | XCB_GC_GRAPHICS_EXPOSURES;
uint32_t values[] = {screen->black_pixel, 0};
xcb_create_gc(conn, result, screen->root, mask, values);
return result;
}
public:
WidgetView(const WidgetView&) = delete;
WidgetView(WidgetView&&) = default;
WidgetView(xcb_connection_t* conn, int screen_number) :
width_(-1), height_(-1),
state_(nullptr),
window_(CreateWindow(conn, width_, height_)),
context_(CreateContext(conn)) {
auto tray = xcb::FindTray(conn, screen_number);
xcb::Embed(conn, tray, window_);
}
void Update(xcb_connection_t* conn) {
if (width_ < 0 || height_ < 0 || !state_) return;
xcb_clear_area(conn, false, window_, 0, 0, width_, height_);
xcb_flush(conn);
for (auto ptr = state_; ptr[0] || ptr[1]; ptr += 2) {
std::vector<xcb_point_t> poly;
for (; ptr[0] || ptr[1]; ptr += 2)
poly.push_back({static_cast<int16_t>(width_ * ptr[0] >> 8), static_cast<int16_t>(height_ * ptr[1] >> 8)});
xcb_fill_poly(conn, window_, context_, XCB_POLY_SHAPE_NONCONVEX, XCB_COORD_MODE_ORIGIN, poly.size(), poly.data());
}
}
void Resize(int width, int height) {
width_ = width;
height_ = height;
}
void SetState(const uint8_t* state) {
state_ = state;
}
bool operator==(xcb_window_t op) const {
return window_ == op;
}
};
using WidgetsBinding = std::list<std::pair<Widget*, WidgetView>>;
void HandleXcbEvent(xcb_connection_t* conn, xcb_generic_event_t* evt, WidgetsBinding& widgets) {
static const auto find_widget = [](auto& widgets, auto window) {
return std::find_if(widgets.begin(), widgets.end(), [window](const auto& op){return op.second == window;});
};
switch (evt->response_type & ~0x80) {
case XCB_EXPOSE: {
auto req = reinterpret_cast<xcb_expose_event_t*>(evt);
auto it = find_widget(widgets, req->window);
if (it != widgets.end()) it->second.Update(conn);
break;
}
case XCB_RESIZE_REQUEST: {
auto req = reinterpret_cast<xcb_resize_request_event_t*>(evt);
auto it = find_widget(widgets, req->window);
if (it != widgets.end()) {
it->second.Resize(req->width, req->height);
it->second.Update(conn);
}
break;
}
default:
break;
}
}
void HandleWidgetEvent(xcb_connection_t* conn, const pollfd& fd, WidgetsBinding& widgets) {
static const auto& find_widget = [](auto& widgets, const auto& fd) {
return std::find_if(widgets.begin(), widgets.end(), [fd](const auto& op) {
const auto& fds = op.first->GetPollFds();
return std::find(fds.begin(), fds.end(), fd) != fds.end();
});
};
auto it = find_widget(widgets, fd);
if (it != widgets.end()) {
it->first->Handle(fd);
it->second.SetState(it->first->GetState());
it->second.Update(conn);
}
}
} // namespace
int main(int argc, char** argv) {
try {
int screen_number;
xcb::Connection conn(xcb_connect(nullptr, &screen_number), &xcb_disconnect);
WidgetsBinding widgets;
for (auto it : WidgetList::Get()) try {
it->Init(argc, argv);
WidgetView view(conn.get(), screen_number);
view.SetState(it->GetState());
widgets.emplace_back(it, std::move(view));
} catch (const std::exception& ex) {
util::PrintException(ex);
}
xcb_flush(conn.get());
for (;;) {
std::vector<pollfd> fds = {{xcb_get_file_descriptor(conn.get()), POLLIN}};
for (const auto& it : widgets) {
const auto& widget_fds = it.first->GetPollFds();
fds.insert(fds.end(), widget_fds.begin(), widget_fds.end());
}
if (poll(fds.data(), fds.size(), -1) < 0)
util::ThrowSystemError("Polling failed");
if (fds[0].revents & POLLIN) {
xcb::Event evt(xcb_poll_for_event(conn.get()), &free);
if (!evt) break;
HandleXcbEvent(conn.get(), evt.get(), widgets);
}
for (const auto& it : fds) {
if (it.revents)
HandleWidgetEvent(conn.get(), it, widgets);
}
xcb_flush(conn.get());
}
return 0;
} catch (const std::exception& ex) {
util::PrintException(ex);
return 1;
}
}