1 /* This file is part of the KDE project
2 * Copyright (C) 2010 Lukas Tinkl <ltinkl@redhat.com>
3 * Copyright (C) 2015 Kai Uwe Broulik <kde@privat.broulik.de>
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License version 2 as published by the Free Software Foundation.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public License
15 * along with this library; see the file COPYING.LIB. If not, write to
16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 *
19 */
20
21
22 #include <QX11Info>
23 #include <QDebug>
24
25 #include "xrandrbrightness.h"
26
XRandrBrightness()27 XRandrBrightness::XRandrBrightness()
28 {
29 if (!QX11Info::isPlatformX11()) {
30 return;
31 }
32 ScopedCPointer<xcb_randr_query_version_reply_t> versionReply(xcb_randr_query_version_reply(QX11Info::connection(),
33 xcb_randr_query_version(QX11Info::connection(), 1, 2),
34 nullptr));
35
36 if (!versionReply) {
37 qDebug() << "RandR Query version returned null";
38 return;
39 }
40
41 if (versionReply->major_version < 1 || (versionReply->major_version == 1 && versionReply->minor_version < 2)) {
42 qDebug() << "RandR version" << versionReply->major_version << "." << versionReply->minor_version << " too old";
43 return;
44 }
45 ScopedCPointer<xcb_intern_atom_reply_t> backlightReply(xcb_intern_atom_reply(QX11Info::connection(),
46 xcb_intern_atom (QX11Info::connection(), 1, strlen("Backlight"), "Backlight"),
47 nullptr));
48
49 if (!backlightReply) {
50 qDebug() << "Intern Atom for Backlight returned null";
51 return;
52 }
53
54 m_backlight = backlightReply->atom;
55
56 if (m_backlight == XCB_NONE) {
57 qDebug() << "No outputs have backlight property";
58 //return;
59 }
60 xcb_screen_iterator_t iter = xcb_setup_roots_iterator(xcb_get_setup(QX11Info::connection()));
61 if (!iter.rem) {
62 qDebug() << "XCB Screen Roots Iterator rem was null";
63 return;
64 }
65
66 xcb_screen_t *screen = iter.data;
67 xcb_window_t root = screen->root;
68
69 m_resources.reset(xcb_randr_get_screen_resources_current_reply(QX11Info::connection(),
70 xcb_randr_get_screen_resources_current(QX11Info::connection(), root)
71 , nullptr));
72
73 if (!m_resources) {
74 qDebug() << "RANDR Get Screen Resources returned null";
75 return;
76 }
77 }
78
79
80
backlight_get_with_range(xcb_randr_output_t output,long & value,long & min,long & max) const81 bool XRandrBrightness::backlight_get_with_range(xcb_randr_output_t output, long &value, long &min, long &max) const {
82 long cur = backlight_get(output);
83 if (cur == -1) {
84 return false;
85 }
86
87 ScopedCPointer<xcb_randr_query_output_property_reply_t> propertyReply(xcb_randr_query_output_property_reply(QX11Info::connection(),
88 xcb_randr_query_output_property(QX11Info::connection(), output, m_backlight)
89 , nullptr));
90
91 if (!propertyReply) {
92 return false;
93 }
94
95 if (propertyReply->range && xcb_randr_query_output_property_valid_values_length(propertyReply.data()) == 2) {
96 int32_t *values = xcb_randr_query_output_property_valid_values(propertyReply.data());
97 value = cur;
98 min = values[0];
99 max = values[1];
100 return true;
101 }
102
103 return false;
104 }
105
backlight_get(xcb_randr_output_t output) const106 long XRandrBrightness::backlight_get(xcb_randr_output_t output) const
107 {
108 ScopedCPointer<xcb_randr_get_output_property_reply_t> propertyReply;
109 long value;
110
111 if (m_backlight != XCB_ATOM_NONE) {
112 propertyReply.reset(xcb_randr_get_output_property_reply(QX11Info::connection(),
113 xcb_randr_get_output_property(QX11Info::connection(), output, m_backlight, XCB_ATOM_NONE, 0, 4, 0, 0)
114 , nullptr));
115
116 if (!propertyReply) {
117 return -1;
118 }
119 }
120
121 if (!propertyReply || propertyReply->type != XCB_ATOM_INTEGER || propertyReply->num_items != 1 || propertyReply->format != 32) {
122 value = -1;
123 } else {
124 value = *(reinterpret_cast<long *>(xcb_randr_get_output_property_data(propertyReply.data())));
125 }
126 return value;
127 }
128
backlight_set(xcb_randr_output_t output,long value)129 void XRandrBrightness::backlight_set(xcb_randr_output_t output, long value)
130 {
131 xcb_randr_change_output_property(QX11Info::connection(), output, m_backlight, XCB_ATOM_INTEGER,
132 32, XCB_PROP_MODE_REPLACE,
133 1, reinterpret_cast<unsigned char *>(&value));
134 }
135
136
gamma_brightness_get(xcb_randr_output_t output)137 float XRandrBrightness::gamma_brightness_get(xcb_randr_output_t output)
138 {
139 xcb_generic_error_t *error;
140
141 xcb_randr_get_output_info_cookie_t output_info_cookie = xcb_randr_get_output_info (QX11Info::connection(), output, 0);
142 ScopedCPointer<xcb_randr_get_output_info_reply_t> output_info(xcb_randr_get_output_info_reply (QX11Info::connection(), output_info_cookie, &error));
143 if(error != nullptr)
144 {
145 qDebug() << "Error getting output_info";
146 return -1;
147 }
148 if(output_info == nullptr)
149 {
150 qDebug() << "Error: output_info is null";
151 return -1;
152 }
153 // xcb_randr_get_output_info_reply_t tiene como elemento crtc
154 xcb_randr_get_crtc_gamma_cookie_t gamma_cookie = xcb_randr_get_crtc_gamma_unchecked (QX11Info::connection(), output_info->crtc);
155 ScopedCPointer<xcb_randr_get_crtc_gamma_reply_t> gamma_reply(xcb_randr_get_crtc_gamma_reply (QX11Info::connection(), gamma_cookie, &error));
156 if(error != nullptr)
157 {
158 qDebug() << "Error getting gamma_reply";
159 return -1;
160 }
161 if(gamma_reply == nullptr)
162 {
163 qDebug() << "Error: gamma_reply is null";
164 return -1;
165 }
166 uint16_t * red = xcb_randr_get_crtc_gamma_red (gamma_reply.data());
167 if(red == nullptr)
168 {
169 qDebug() << "Error: red is null";
170 return -1;
171 }
172 int red_length = xcb_randr_get_crtc_gamma_red_length(gamma_reply.data());
173
174 // uint16_t *green = xcb_randr_get_crtc_gamma_green (gamma_reply);
175 // if(green == NULL)
176 // {
177 // qDebug() << "Error: green is null";
178 // return -1;
179 // }
180 // uint16_t *blue = xcb_randr_get_crtc_gamma_blue (gamma_reply);
181 // if(blue == NULL)
182 // {
183 // qDebug() << "Error: blue is null";
184 // return -1;
185 // }
186
187 float brightness = (float)red[red_length-1]/65535.0;
188 return brightness;
189 }
190
gamma_brightness_set(xcb_randr_output_t output,float percent)191 void XRandrBrightness::gamma_brightness_set(xcb_randr_output_t output, float percent)
192 {
193 xcb_generic_error_t *error;
194
195 xcb_randr_get_output_info_cookie_t output_info_cookie = xcb_randr_get_output_info (QX11Info::connection(), output, 0);
196 ScopedCPointer<xcb_randr_get_output_info_reply_t> output_info(xcb_randr_get_output_info_reply (QX11Info::connection(), output_info_cookie, &error));
197 if(error != nullptr)
198 {
199 qDebug() << "Error getting output_info";
200 return;
201 }
202 if(output_info == nullptr)
203 {
204 qDebug() << "Error: output_info is null";
205 return;
206 }
207 // xcb_randr_get_output_info_reply_t tiene como elemento crtc
208 xcb_randr_get_crtc_gamma_cookie_t gamma_cookie = xcb_randr_get_crtc_gamma_unchecked (QX11Info::connection(), output_info->crtc);
209 ScopedCPointer<xcb_randr_get_crtc_gamma_reply_t> gamma_reply(xcb_randr_get_crtc_gamma_reply (QX11Info::connection(), gamma_cookie, &error));
210 if(error != nullptr)
211 {
212 qDebug() << "Error getting gamma_reply";
213 return;
214 }
215 if(gamma_reply == nullptr)
216 {
217 qDebug() << "Error: gamma_reply is null";
218 return;
219 }
220 uint16_t *red = xcb_randr_get_crtc_gamma_red (gamma_reply.data());
221 if(red == nullptr)
222 {
223 qDebug() << "Error: red is null";
224 return;
225 }
226 int red_length = xcb_randr_get_crtc_gamma_red_length(gamma_reply.data());
227 uint16_t *green = xcb_randr_get_crtc_gamma_green (gamma_reply.data());
228 if(green == nullptr)
229 {
230 qDebug() << "Error: green is null";
231 return;
232 }
233 uint16_t *blue = xcb_randr_get_crtc_gamma_blue (gamma_reply.data());
234 if(blue == nullptr)
235 {
236 qDebug() << "Error: blue is null";
237 return;
238 }
239
240 float max_gamma = 65535*percent;
241 for(int i=0;i<red_length;i++)
242 {
243 int value = qMin((int)(((float)i/(float)(red_length-1))*max_gamma),65535);
244 green[i] = blue[i] = red[i] = value;
245 }
246 xcb_randr_set_crtc_gamma (QX11Info::connection(), output_info->crtc, red_length, red, green, blue);
247 }
248
249
getMonitorsInfo()250 QList<MonitorInfo> XRandrBrightness::getMonitorsInfo()
251 {
252 QList<MonitorInfo> monitors;
253
254 if (!m_resources) {
255 return monitors;
256 }
257
258 auto *outputs = xcb_randr_get_screen_resources_current_outputs(m_resources.data());
259 for (int i = 0; i < m_resources->num_outputs; ++i) {
260 xcb_randr_output_t output = outputs[i];
261
262 xcb_generic_error_t *error;
263
264 xcb_randr_get_output_info_cookie_t output_info_cookie = xcb_randr_get_output_info (QX11Info::connection(), output, 0);
265 ScopedCPointer <xcb_randr_get_output_info_reply_t> output_info(xcb_randr_get_output_info_reply (QX11Info::connection(), output_info_cookie, &error));
266 if(error != nullptr)
267 {
268 qDebug() << "Error getting output_info";
269 continue;
270 }
271 if(output_info == nullptr)
272 {
273 qDebug() << "Error: output_info is null";
274 continue;
275 }
276
277 QString name = QString::fromUtf8((const char *) xcb_randr_get_output_info_name(output_info.data()), output_info->name_len);
278
279
280 qDebug() << "Found output:" << name;
281
282
283 // Is connected?
284 if ( (xcb_randr_connection_t)(output_info->connection) != XCB_RANDR_CONNECTION_CONNECTED )
285 {
286 qDebug() << "Output is not connected";
287 continue; // This output is not connected. Check other
288 }
289
290 // Is enabled?
291 if( output_info->crtc == 0)
292 {
293 qDebug() << "Crtc is not null. Output not enabled.";
294 continue;
295 }
296 xcb_randr_get_crtc_info_cookie_t crtc_info_cookie = xcb_randr_get_crtc_info_unchecked (QX11Info::connection(), output_info->crtc, 0);
297 ScopedCPointer<xcb_randr_get_crtc_info_reply_t> crtc_info(xcb_randr_get_crtc_info_reply (QX11Info::connection(), crtc_info_cookie, &error));
298 if(error != nullptr)
299 {
300 qDebug() << "Error getting output_info";
301 continue;
302 }
303 if(crtc_info == nullptr)
304 {
305 qDebug() << "Error: output_info is null";
306 continue;
307 }
308 if( crtc_info->mode == XCB_NONE )
309 {
310 qDebug() << "No modes. Output not enabled.";
311 continue;
312 }
313
314 // Output is connected and enabled. Get data:
315 bool backlightIsSuported = false;
316 long cur, min, max, backlight_max = -1;
317 if (backlight_get(output) != -1)
318 {
319 if (backlight_get_with_range(output, cur, min, max))
320 {
321 backlightIsSuported = true;
322 backlight_max = max - min;
323 }
324 }
325
326 MonitorInfo monitor((int)output, name, backlight_max);
327
328 if(backlightIsSuported)
329 monitor.setBacklight(cur-min);
330
331 monitor.setBrightness(gamma_brightness_get(output));
332
333 qDebug() << "Output:" << name << "added";
334 monitors.append(monitor);
335
336 }
337
338 return monitors;
339 }
340
setMonitorsSettings(QList<MonitorInfo> monitors)341 void XRandrBrightness::setMonitorsSettings(QList<MonitorInfo> monitors)
342 {
343 if (!m_resources) {
344 return;
345 }
346
347 auto *outputs = xcb_randr_get_screen_resources_current_outputs(m_resources.data());
348 for (int i = 0; i < m_resources->num_outputs; ++i) {
349 xcb_randr_output_t output = outputs[i];
350
351 xcb_generic_error_t *error;
352
353 xcb_randr_get_output_info_cookie_t output_info_cookie = xcb_randr_get_output_info (QX11Info::connection(), output, 0);
354 ScopedCPointer<xcb_randr_get_output_info_reply_t> output_info(xcb_randr_get_output_info_reply (QX11Info::connection(), output_info_cookie, &error));
355 if(error != nullptr)
356 {
357 qDebug() << "Error getting output_info";
358 continue;
359 }
360 if(output_info == nullptr)
361 {
362 qDebug() << "Error: output_info is null";
363 continue;
364 }
365
366 // Is connected?
367 if ( (xcb_randr_connection_t)(output_info->connection) != XCB_RANDR_CONNECTION_CONNECTED )
368 continue; // This output is not connected. Check other
369
370 // Is enabled?
371 if( output_info->crtc == 0)
372 continue;
373 xcb_randr_get_crtc_info_cookie_t crtc_info_cookie = xcb_randr_get_crtc_info_unchecked (QX11Info::connection(), output_info->crtc, 0);
374 ScopedCPointer<xcb_randr_get_crtc_info_reply_t> crtc_info(xcb_randr_get_crtc_info_reply (QX11Info::connection(), crtc_info_cookie, &error));
375 if(error != nullptr)
376 continue;
377 if(crtc_info == nullptr || crtc_info->mode == XCB_NONE )
378 continue;
379
380 QString name = QString::fromUtf8((const char *) xcb_randr_get_output_info_name(output_info.data()), output_info->name_len);
381
382 // Output is connected and enabled. Get data:
383 bool backlightIsSuported = false;
384 long cur, min, max, backlight_value = 0;
385 if (backlight_get(output) != -1)
386 {
387 if (backlight_get_with_range(output, cur, min, max))
388 {
389 backlightIsSuported = true;
390 backlight_value = cur - min;
391 }
392 }
393 float brightness_value = gamma_brightness_get(output);
394
395 // Compare output info with settings and set it.
396 for(const MonitorInfo &monitor: monitors)
397 {
398 //qDebug() << "[XRandrBrightness::setMonitorsSettings]" << monitor.id() << (int)output << monitor.name() << name ;
399 if(monitor.id() == (int)output && monitor.name() == name)
400 {
401 // Set settings
402 if(backlightIsSuported && monitor.backlight() != backlight_value)
403 backlight_set(output, min+monitor.backlight());
404 if(monitor.brightness() != brightness_value)
405 gamma_brightness_set(output, monitor.brightness());
406 break;
407 }
408 }
409 }
410 }
411
412