-
Notifications
You must be signed in to change notification settings - Fork 404
/
randr.c
299 lines (248 loc) · 8.71 KB
/
randr.c
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
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
/*
* vim:ts=4:sw=4:expandtab
*
* © 2010 Michael Stapelberg
*
* See LICENSE for licensing information
*
*/
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <xcb/xcb.h>
#include <xcb/xinerama.h>
#include <xcb/randr.h>
#include "i3lock.h"
#include "xcb.h"
#include "randr.h"
/* Number of Xinerama screens which are currently present. */
int xr_screens = 0;
/* The resolutions of the currently present Xinerama screens. */
Rect *xr_resolutions = NULL;
static bool xinerama_active;
static bool has_randr = false;
static bool has_randr_1_5 = false;
extern bool debug_mode;
void _xinerama_init(void);
void randr_init(int *event_base, xcb_window_t root) {
const xcb_query_extension_reply_t *extreply;
extreply = xcb_get_extension_data(conn, &xcb_randr_id);
if (!extreply->present) {
DEBUG("RandR is not present, falling back to Xinerama.\n");
_xinerama_init();
return;
}
xcb_generic_error_t *err;
xcb_randr_query_version_reply_t *randr_version =
xcb_randr_query_version_reply(
conn, xcb_randr_query_version(conn, XCB_RANDR_MAJOR_VERSION, XCB_RANDR_MINOR_VERSION), &err);
if (err != NULL) {
DEBUG("Could not query RandR version: X11 error code %d\n", err->error_code);
_xinerama_init();
return;
}
has_randr = true;
has_randr_1_5 = (randr_version->major_version >= 1) &&
(randr_version->minor_version >= 5);
free(randr_version);
if (event_base != NULL) {
*event_base = extreply->first_event;
}
xcb_randr_select_input(conn, root,
XCB_RANDR_NOTIFY_MASK_SCREEN_CHANGE |
XCB_RANDR_NOTIFY_MASK_OUTPUT_CHANGE |
XCB_RANDR_NOTIFY_MASK_CRTC_CHANGE |
XCB_RANDR_NOTIFY_MASK_OUTPUT_PROPERTY);
xcb_flush(conn);
}
void _xinerama_init(void) {
if (!xcb_get_extension_data(conn, &xcb_xinerama_id)->present) {
DEBUG("Xinerama extension not found, disabling.\n");
return;
}
xcb_xinerama_is_active_cookie_t cookie;
xcb_xinerama_is_active_reply_t *reply;
cookie = xcb_xinerama_is_active(conn);
reply = xcb_xinerama_is_active_reply(conn, cookie, NULL);
if (!reply) {
return;
}
if (!reply->state) {
free(reply);
return;
}
xinerama_active = true;
free(reply);
}
/*
* randr_query_outputs_15 uses RandR ≥ 1.5 to update outputs.
*
*/
static bool _randr_query_monitors_15(xcb_window_t root) {
#if XCB_RANDR_MINOR_VERSION < 5
return false;
#else
/* RandR 1.5 available at compile-time, i.e. libxcb is new enough */
if (!has_randr_1_5) {
return false;
}
/* RandR 1.5 available at run-time (supported by the server) */
DEBUG("Querying monitors using RandR 1.5\n");
xcb_generic_error_t *err;
xcb_randr_get_monitors_reply_t *monitors =
xcb_randr_get_monitors_reply(
conn, xcb_randr_get_monitors(conn, root, true), &err);
if (err != NULL) {
DEBUG("Could not get RandR monitors: X11 error code %d\n", err->error_code);
free(err);
/* Fall back to RandR ≤ 1.4 */
return false;
}
int screens = xcb_randr_get_monitors_monitors_length(monitors);
DEBUG("%d RandR monitors found (timestamp %d)\n",
screens, monitors->timestamp);
Rect *resolutions = malloc(screens * sizeof(Rect));
/* No memory? Just keep on using the old information. */
if (!resolutions) {
free(monitors);
return true;
}
xcb_randr_monitor_info_iterator_t iter;
int screen;
for (iter = xcb_randr_get_monitors_monitors_iterator(monitors), screen = 0;
iter.rem;
xcb_randr_monitor_info_next(&iter), screen++) {
const xcb_randr_monitor_info_t *monitor_info = iter.data;
resolutions[screen].x = monitor_info->x;
resolutions[screen].y = monitor_info->y;
resolutions[screen].width = monitor_info->width;
resolutions[screen].height = monitor_info->height;
DEBUG("found RandR monitor: %d x %d at %d x %d\n",
monitor_info->width, monitor_info->height,
monitor_info->x, monitor_info->y);
}
free(xr_resolutions);
xr_resolutions = resolutions;
xr_screens = screens;
free(monitors);
return true;
#endif
}
/*
* randr_query_outputs_14 uses RandR ≤ 1.4 to update outputs.
*
*/
static bool _randr_query_outputs_14(xcb_window_t root) {
if (!has_randr) {
return false;
}
DEBUG("Querying outputs using RandR ≤ 1.4\n");
/* Get screen resources (primary output, crtcs, outputs, modes) */
xcb_randr_get_screen_resources_current_cookie_t rcookie;
rcookie = xcb_randr_get_screen_resources_current(conn, root);
xcb_randr_get_screen_resources_current_reply_t *res =
xcb_randr_get_screen_resources_current_reply(conn, rcookie, NULL);
if (res == NULL) {
DEBUG("Could not query screen resources.\n");
return false;
}
/* timestamp of the configuration so that we get consistent replies to all
* requests (if the configuration changes between our different calls) */
const xcb_timestamp_t cts = res->config_timestamp;
const int len = xcb_randr_get_screen_resources_current_outputs_length(res);
/* an output is VGA-1, LVDS-1, etc. (usually physical video outputs) */
xcb_randr_output_t *randr_outputs = xcb_randr_get_screen_resources_current_outputs(res);
/* Request information for each output */
xcb_randr_get_output_info_cookie_t ocookie[len];
for (int i = 0; i < len; i++) {
ocookie[i] = xcb_randr_get_output_info(conn, randr_outputs[i], cts);
}
Rect *resolutions = malloc(len * sizeof(Rect));
/* No memory? Just keep on using the old information. */
if (!resolutions) {
free(res);
return true;
}
/* Loop through all outputs available for this X11 screen */
int screen = 0;
for (int i = 0; i < len; i++) {
xcb_randr_get_output_info_reply_t *output;
if ((output = xcb_randr_get_output_info_reply(conn, ocookie[i], NULL)) == NULL) {
continue;
}
if (output->crtc == XCB_NONE) {
free(output);
continue;
}
xcb_randr_get_crtc_info_cookie_t icookie;
xcb_randr_get_crtc_info_reply_t *crtc;
icookie = xcb_randr_get_crtc_info(conn, output->crtc, cts);
if ((crtc = xcb_randr_get_crtc_info_reply(conn, icookie, NULL)) == NULL) {
DEBUG("Skipping output: could not get CRTC (0x%08x)\n", output->crtc);
free(output);
continue;
}
resolutions[screen].x = crtc->x;
resolutions[screen].y = crtc->y;
resolutions[screen].width = crtc->width;
resolutions[screen].height = crtc->height;
DEBUG("found RandR output: %d x %d at %d x %d\n",
crtc->width, crtc->height,
crtc->x, crtc->y);
screen++;
free(crtc);
free(output);
}
free(xr_resolutions);
xr_resolutions = resolutions;
xr_screens = screen;
free(res);
return true;
}
void _xinerama_query_screens(void) {
if (!xinerama_active) {
return;
}
xcb_xinerama_query_screens_cookie_t cookie;
xcb_xinerama_query_screens_reply_t *reply;
xcb_xinerama_screen_info_t *screen_info;
xcb_generic_error_t *err;
cookie = xcb_xinerama_query_screens_unchecked(conn);
reply = xcb_xinerama_query_screens_reply(conn, cookie, &err);
if (!reply) {
DEBUG("Couldn't get Xinerama screens: X11 error code %d\n", err->error_code);
free(err);
return;
}
screen_info = xcb_xinerama_query_screens_screen_info(reply);
int screens = xcb_xinerama_query_screens_screen_info_length(reply);
Rect *resolutions = malloc(screens * sizeof(Rect));
/* No memory? Just keep on using the old information. */
if (!resolutions) {
free(reply);
return;
}
for (int screen = 0; screen < xr_screens; screen++) {
resolutions[screen].x = screen_info[screen].x_org;
resolutions[screen].y = screen_info[screen].y_org;
resolutions[screen].width = screen_info[screen].width;
resolutions[screen].height = screen_info[screen].height;
DEBUG("found Xinerama screen: %d x %d at %d x %d\n",
screen_info[screen].width, screen_info[screen].height,
screen_info[screen].x_org, screen_info[screen].y_org);
}
free(xr_resolutions);
xr_resolutions = resolutions;
xr_screens = screens;
free(reply);
}
void randr_query(xcb_window_t root) {
if (_randr_query_monitors_15(root)) {
return;
}
if (_randr_query_outputs_14(root)) {
return;
}
_xinerama_query_screens();
}