summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimon Rettberg2018-08-15 17:52:41 +0200
committerSimon Rettberg2018-08-15 17:52:41 +0200
commitc46a9a1272f3b9265759c91ce2e1e6c7836fe74c (patch)
tree7477e9e15f7ef0782609975d811692db7a405b79
parentFix conditional bring-to-top: Only when dropdown is visible, not control (diff)
downloadbeamergui-c46a9a1272f3b9265759c91ce2e1e6c7836fe74c.tar.gz
beamergui-c46a9a1272f3b9265759c91ce2e1e6c7836fe74c.tar.xz
beamergui-c46a9a1272f3b9265759c91ce2e1e6c7836fe74c.zip
New Version, WIP
-rw-r--r--CMakeLists.txt2
-rw-r--r--src/cvt.c552
-rw-r--r--src/cvt.h26
-rw-r--r--src/i18n/de.ts72
-rw-r--r--src/main.cpp101
-rw-r--r--src/main.h12
-rw-r--r--src/widget.cpp588
-rw-r--r--src/widget.h49
-rw-r--r--src/widget.ui206
-rw-r--r--src/x.cpp1062
-rw-r--r--src/x.h337
11 files changed, 1748 insertions, 1259 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 65a4e72..448676c 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -26,7 +26,7 @@ endif()
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOMOC ON)
-file(GLOB_RECURSE BEAMERGUI_SOURCES src/*.cpp)
+file(GLOB_RECURSE BEAMERGUI_SOURCES src/*.cpp src/*.c)
file(GLOB_RECURSE BEAMERGUI_UIS src/*.ui)
file(GLOB_RECURSE BEAMERGUI_RESOURCES src/*.qrc)
file(GLOB_RECURSE BEAMERGUI_TSS src/i18n/*.ts)
diff --git a/src/cvt.c b/src/cvt.c
new file mode 100644
index 0000000..9563f0f
--- /dev/null
+++ b/src/cvt.c
@@ -0,0 +1,552 @@
+#include "cvt.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Based on the cvt util:
+ * http://www.uruk.org/projects/cvt/cvt.c
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#define CLOCK_STEP 0.25 /* Clock steps in MHz */
+#define MARGIN_PERCENT 1.8 /* % of active vertical image */
+#define H_SYNC_PER 8.0 /* sync % of horizontal image */
+#define CELL_GRAN 8.4999 /* assumed character cell granularity */
+#define CELL_GRAN_RND 8.0 /* assumed character cell granularity (round)*/
+#define MIN_V_BPORCH 3.0 /* width of vsync in lines */
+#define MIN_V_PORCH_RND 3.0 /* width of vsync in lines */
+#define M 600.0 /* blanking formula gradient */
+#define C 40.0 /* blanking formula offset */
+#define K 128.0 /* blanking formula scaling factor */
+#define J 20.0 /* blanking formula scaling factor */
+
+/* Standard Timing Parameters */
+#define MIN_VSYNC_BP 550.0 /* min time of vsync + back porch (us) */
+#define H_SYNC_PERCENT 8.0 /* width of hsync as % of total line */
+
+/* Reduced Blanking defines */
+#define RB_MIN_V_BPORCH 6.0 /* lines */
+#define RB_V_FPORCH 3.0 /* lines */
+#define RB_MIN_V_BLANK 460.0 /* us */
+#define RB_H_SYNC 32.0 /* pixels */
+#define RB_H_BLANK 160.0 /* pixels */
+
+/* C' and M' are part of the Blanking Duty Cycle computation */
+
+#define C_PRIME (((C - J) * K/256.0) + J)
+#define M_PRIME (K/256.0 * M)
+
+/* NOP out prints */
+#define print_value(...) (void)0
+
+typedef struct __options
+{
+ int x, y;
+ int reduced_blank, interlaced;
+ int xf86mode, fbmode;
+ float v_freq;
+} options;
+
+
+/*
+ * vert_refresh() - as defined by the CVT Timing Standard, compute the
+ * Stage 1 Parameters using the vertical refresh frequency. In other
+ * words: input a desired resolution and desired refresh rate, and
+ * output the CVT mode timings.
+ *
+ * XXX margin computations are implemented but not tested (nor used by
+ * XFree86 of fbset mode descriptions, from what I can tell).
+ */
+
+mode *vert_refresh (int h_pixels, int v_lines, float freq,
+ int interlaced, int reduced_blank, int margins)
+{
+ float h_pixels_rnd;
+ float v_lines_rnd;
+ float v_field_rate_rqd;
+ float top_margin;
+ float bot_margin;
+ float interlace;
+ float h_period_est;
+ float v_sync_bp;
+ float total_v_lines;
+ float left_margin;
+ float right_margin;
+ float total_active_pixels;
+ float ideal_duty_cycle;
+ float h_blank;
+ float total_pixels;
+
+ float cur_duty_cycle;
+ float v_sync;
+ float v_sync_rnd, h_sync_rnd;
+ float h_back_porch, v_front_porch, h_front_porch;
+
+ float vbi_lines, act_vbi_lines, rb_min_vbi;
+ float act_pixel_freq, act_h_freq;
+ float act_field_rate, act_frame_rate;
+ char *aspect_ratio;
+ int stage;
+
+ mode *m = (mode*) malloc (sizeof (mode));
+
+
+ /* 1. Required Field Rate
+ *
+ * This is slightly different from the spreadsheet because we use
+ * a different result for interlaced video modes. Simplifies this
+ * to the input field rate.
+ *
+ * [V FIELD RATE RQD] = [I/P FREQ RQD]
+ */
+
+ v_field_rate_rqd = freq;
+
+ print_value(1, "[V FIELD RATE RQD]", v_field_rate_rqd);
+
+
+ /* 2. Horizontal Pixels
+ *
+ * In order to give correct results, the number of horizontal
+ * pixels requested is first processed to ensure that it is divisible
+ * by the character size, by rounding it to the nearest character
+ * cell boundary.
+ *
+ * [H PIXELS RND] = ((ROUNDDOWN([H PIXELS]/[CELL GRAN RND],0))
+ * *[CELLGRAN RND])
+ */
+
+ h_pixels_rnd = floor((float) h_pixels / CELL_GRAN_RND) * CELL_GRAN_RND;
+
+ print_value(2, "[H PIXELS RND]", h_pixels_rnd);
+
+
+ /* 2.5th Calculation, aspect_ratio & v_sync_rnd
+ *
+ * [ASPECT_RATIO] = IF(H_PIXELS_RND = CELL_GRAN_RND*ROUND((V_LINES*
+ * 4.0/3.0)/CELL_GRAN_RND),"4:3")
+ * etc...
+ * [V_SYNC] = [value from table based on aspect ratio]
+ * [V_SYNC_RND] = ROUND(V_SYNC,0) // Not needed in principle
+ */
+
+ if (h_pixels_rnd == CELL_GRAN_RND * floor(((float)v_lines * 4.0 / 3.0)
+ / CELL_GRAN_RND)) {
+ aspect_ratio = "4:3";
+ v_sync = 4;
+ } else if (h_pixels_rnd == CELL_GRAN_RND * floor(((float)v_lines * 16.0
+ / 9.0) / CELL_GRAN_RND)) {
+ aspect_ratio = "16:9";
+ v_sync = 5;
+ } else if (h_pixels_rnd == CELL_GRAN_RND * floor(((float)v_lines * 16.0
+ / 10.0) / CELL_GRAN_RND)) {
+ aspect_ratio = "16:10";
+ v_sync = 6;
+ } else if (h_pixels_rnd == CELL_GRAN_RND * floor(((float)v_lines * 5.0
+ / 4.0) / CELL_GRAN_RND)) {
+ aspect_ratio = "5:4";
+ v_sync = 7;
+ } else if (h_pixels_rnd == CELL_GRAN_RND * floor(((float)v_lines * 15.0
+ / 9.0) / CELL_GRAN_RND)) {
+ aspect_ratio = "15:9";
+ v_sync = 7;
+ } else {
+ /* Default case of unknown aspect ratio */
+ aspect_ratio = "Custom";
+ v_sync = 10;
+ }
+ v_sync_rnd = v_sync;
+
+ /*
+ * 3. Determine Left & Right Borders
+ *
+ * Calculate the margins on the left and right side.
+ *
+ * [LEFT MARGIN (PIXELS)] = (IF( [MARGINS RQD?]="Y",
+ * (ROUNDDOWN( ([H PIXELS RND] * [MARGIN%] / 100 /
+ * [CELL GRAN RND]),0)) * [CELL GRAN RND],
+ * 0))
+ * [RIGHT MARGIN (PIXELS)] = (IF( [MARGINS RQD?]="Y",
+ * (ROUNDDOWN( ([H PIXELS RND] * [MARGIN%] / 100 /
+ * [CELL GRAN RND]),0)) * [CELL GRAN RND],
+ * 0))
+ */
+
+ left_margin = margins ?
+ floor(h_pixels_rnd * MARGIN_PERCENT / 100.0 / CELL_GRAN_RND)
+ * CELL_GRAN_RND : 0.0;
+ right_margin = left_margin;
+
+ print_value(3, "[LEFT MARGIN (PIXELS)]", left_margin);
+ print_value(3, "[RIGHT MARGIN (PIXELS)]", right_margin);
+
+
+ /* 4. Find total active pixels.
+ *
+ * Find total number of active pixels in image and left and right
+ * margins.
+ *
+ * [TOTAL ACTIVE PIXELS] = [H PIXELS RND] + [LEFT MARGIN (PIXELS)] +
+ * [RIGHT MARGIN (PIXELS)]
+ */
+
+ total_active_pixels = h_pixels_rnd + left_margin + right_margin;
+
+ print_value(4, "[TOTAL ACTIVE PIXELS]", total_active_pixels);
+
+
+ /* 5. Find number of lines per field.
+ *
+ * If interlace is requested, the number of vertical lines assumed
+ * by the calculation must be halved, as the computation calculates
+ * the number of vertical lines per field. In either case, the
+ * number of lines is rounded to the nearest integer.
+ *
+ * [V LINES RND] = IF([INT RQD?]="y", ROUNDDOWN([V LINES]/2,0),
+ * ROUNDDOWN([V LINES],0))
+ */
+
+ v_lines_rnd = interlaced ?
+ floor((float) v_lines / 2.0) :
+ floor((float) v_lines);
+
+ print_value(5, "[V LINES RND]", v_lines_rnd);
+
+
+ /* 6. Find Top and Bottom margins.
+ *
+ * [TOP MARGIN (LINES)] = IF([MARGINS RQD?]="Y",
+ * ROUNDDOWN(([MARGIN%]/100*[V LINES RND]),0),
+ * 0)
+ * [BOT MARGIN (LINES)] = IF([MARGINS RQD?]="Y",
+ * ROUNDDOWN(([MARGIN%]/100*[V LINES RND]),0),
+ * 0)
+ */
+
+ top_margin = margins ? floor(MARGIN_PERCENT / 100.0 * v_lines_rnd) : (0.0);
+ bot_margin = top_margin;
+
+ print_value(6, "[TOP MARGIN (LINES)]", top_margin);
+ print_value(6, "[BOT MARGIN (LINES)]", bot_margin);
+
+
+ /* 7. If interlace is required, then set variable [INTERLACE]=0.5:
+ *
+ * [INTERLACE]=(IF([INT RQD?]="y",0.5,0))
+ */
+
+ interlace = interlaced ? 0.5 : 0.0;
+
+ print_value(7, "[INTERLACE]", interlace);
+
+
+ /*
+ * Here it diverges for "reduced blanking" or normal blanking modes.
+ */
+
+ if (reduced_blank) {
+ h_blank = RB_H_BLANK;
+
+
+ /* 8. Estimate Horiz. Period (us).
+ *
+ * [H PERIOD EST] = ((1000000/V_FIELD_RATE_RQD)-RB_MIN_V_BLANK)/(V_LINES_RND+TOP_MARGIN+BOT_MARGIN)
+ */
+
+ h_period_est = (1000000.0/v_field_rate_rqd - RB_MIN_V_BLANK)
+ / (v_lines_rnd + top_margin + bot_margin);
+
+ print_value(8, "[H PERIOD EST]", h_period_est);
+
+
+ /* 9. Find number of lines in vertical blanking.
+ *
+ * [Actual VBI_LINES] = RB_MIN_V_BLANK/H_PERIOD_EST
+ * [VBI_LINES] = ROUNDDOWN(RB_MIN_V_BLANK/H_PERIOD_EST,0) + 1
+ */
+
+ vbi_lines = RB_MIN_V_BLANK/h_period_est;
+ print_value(9, "[Actual VBI LINES]", vbi_lines);
+
+ vbi_lines = floor(vbi_lines) + 1.0;
+ print_value(9, "[VBI LINES]", vbi_lines);
+
+
+ /* 10. Check Vertical Blanking is sufficient.
+ *
+ * [RB MIN VBI] = RB_V_FPORCH+V_SYNC_RND+RB_MIN_V_BPORCH
+ * [ACT VBI LINES] = IF(VBI_LINES<RB_MIN_VBI,RB_MIN_VBI,VBI_LINES)
+ */
+
+ rb_min_vbi = RB_V_FPORCH + v_sync_rnd + RB_MIN_V_BPORCH;
+ act_vbi_lines = (vbi_lines < rb_min_vbi) ? rb_min_vbi : vbi_lines;
+
+ print_value(10, "[Minimum VBI Lines]", rb_min_vbi);
+ print_value(10, "[ACT VBI LINES]", act_vbi_lines);
+
+
+ /* 11. Find total number of lines in vertical field.
+ *
+ * [TOTAL V LINES] = ACT_VBI_LINES+V_LINES_RND+TOP_MARGIN+BOT_MARGIN+INTERLACE
+ */
+
+ total_v_lines = act_vbi_lines + v_lines_rnd + top_margin
+ + bot_margin + interlace;
+
+ print_value(11, "[TOTAL V LINES]", total_v_lines);
+
+
+ /* 12. Find total number of pixels in a line (pixels).
+ *
+ * [TOTAL PIXELS] = RB_H_BLANK+TOTAL_ACTIVE_PIXELS
+ */
+
+ total_pixels = total_active_pixels + RB_H_BLANK;
+
+ print_value(12, "[TOTAL PIXELS]", total_pixels);
+
+
+ /* 13. Find Pixel Clock Frequency (MHz).
+ *
+ * [Non-rounded PIXEL_FREQ] = V_FIELD_RATE_RQD*TOTAL_V_LINES*TOTAL_PIXELS/1000000
+ * [ACT PIXEL FREQ] = CLOCK_STEP * ROUND((V_FIELD_RATE_RQD*TOTAL_V_LINES*TOTAL_PIXELS/1000000)/CLOCK_STEP,0)
+ */
+
+ act_pixel_freq = v_field_rate_rqd * total_v_lines
+ * total_pixels / 1000000.0;
+ print_value(13, "[Non-rounded PIXEL FREQ]", act_pixel_freq);
+
+ act_pixel_freq = CLOCK_STEP * floor(act_pixel_freq / CLOCK_STEP);
+ print_value(13, "[ACT PIXEL FREQ]", act_pixel_freq);
+
+
+ stage = 14;
+
+ } else { /* Normal Blanking */
+
+ /* 8. Estimate Horiz. Period (us).
+ *
+ * [H PERIOD EST] = ((1/V_FIELD_RATE_RQD)-MIN_VSYNC_BP/1000000)/(V_LINES_RND+(2*TOP_MARGIN)+MIN_V_PORCH_RND+INTERLACE)*1000000
+ */
+
+ h_period_est = ((1/v_field_rate_rqd) - MIN_VSYNC_BP/1000000.0)
+ / (v_lines_rnd + (2*top_margin) + MIN_V_PORCH_RND + interlace)
+ * 1000000.0;
+
+ print_value(8, "[H PERIOD EST]", h_period_est);
+
+
+ /* 9. Find number of lines in (SYNC + BACK PORCH).
+ *
+ * [Estimated V_SYNC_BP] = ROUNDDOWN((MIN_VSYNC_BP/H_PERIOD_EST),0)+1
+ * [Actual V_SYNC_BP] = MIN_VSYNC_BP/H_PERIOD_EST
+ * [V_SYNC_BP] = IF(Estimated V_SYNC_BP<(V_SYNC+MIN_V_BPORCH),
+ * V_SYNC+MIN_V_BPORCH,Estimated V_SYNC_BP)
+ */
+
+ v_sync_bp = MIN_VSYNC_BP/h_period_est;
+ print_value(9, "[Actual V_SYNC_BP]", v_sync_bp);
+
+ v_sync_bp = floor(v_sync_bp) + 1;
+ print_value(9, "[Estimated V_SYNC_BP]", v_sync_bp);
+
+ v_sync_bp = (v_sync_bp < v_sync + MIN_V_BPORCH) ?
+ v_sync + MIN_V_BPORCH : v_sync_bp;
+ print_value(9, "[V_SYNC_BP]", v_sync_bp);
+
+
+ /* 10. Find number of lines in back porch (Lines).
+ *
+ * [Back porch] = V_SYNC_BP - V_SYNC_RND;
+ */
+
+ print_value(10, "[Back porch]", v_sync_bp - v_sync_rnd);
+
+
+ /* 11. Find total number of lines in vertical field.
+ *
+ * [TOTAL V LINES] = V_LINES_RND+TOP_MARGIN+BOT_MARGIN
+ * +V_SYNC_BP+INTERLACE+MIN_V_PORCH_RND
+ */
+
+ total_v_lines = v_lines_rnd + top_margin + bot_margin
+ + v_sync_bp + interlace + MIN_V_PORCH_RND;
+
+ print_value(11, "[TOTAL V LINES]", total_v_lines);
+
+
+ /* 12. Find ideal blanking duty cycle from formula (%):
+ *
+ * [IDEAL DUTY CYCLE] = C_PRIME-(M_PRIME*H_PERIOD_EST/1000)
+ */
+
+ ideal_duty_cycle = C_PRIME - (M_PRIME * h_period_est / 1000.0);
+
+ print_value(12, "[IDEAL DUTY CYCLE]", ideal_duty_cycle);
+
+
+ /* 13. Find blanking time to nearest cell (Pixels).
+ *
+ * [H BLANK] = IF(IDEAL_DUTY_CYCLE<20,(ROUNDDOWN((TOTAL_ACTIVE_PIXELS*20/(100-20)/(2*CELL_GRAN_RND)),0))*(2*CELL_GRAN_RND),(ROUNDDOWN((TOTAL_ACTIVE_PIXELS*IDEAL_DUTY_CYCLE/(100-IDEAL_DUTY_CYCLE)/(2*CELL_GRAN_RND)),0))*(2*CELL_GRAN_RND))
+ */
+
+ cur_duty_cycle = (ideal_duty_cycle < 20.0) ? 20.0 : ideal_duty_cycle;
+ h_blank = floor((total_active_pixels * cur_duty_cycle/(100.0 - cur_duty_cycle)/(2.0*CELL_GRAN_RND))) * (2.0*CELL_GRAN_RND);
+
+ print_value(13, "[H BLANK]", h_blank);
+
+
+ /* 14. Find total number of pixels in a line (Pixels).
+ *
+ * [TOTAL PIXELS] = TOTAL_ACTIVE_PIXELS + H_BLANK
+ */
+
+ total_pixels = total_active_pixels + h_blank;
+
+ print_value(14, "[TOTAL PIXELS]", total_pixels);
+
+
+ /* 15. Find pixel clock frequency (MHz).
+ *
+ * [Non-rounded PIXEL FREQ] = TOTAL_PIXELS / H_PERIOD_EST
+ * [ACT PIXEL FREQ] = CLOCK_STEP * ROUNDDOWN(
+ */
+
+ act_pixel_freq = total_pixels / h_period_est;
+ print_value(15, "[Non-rounded PIXEL FREQ]", act_pixel_freq);
+
+ act_pixel_freq = CLOCK_STEP * floor(act_pixel_freq / CLOCK_STEP);
+ print_value(15, "[ACT PIXEL FREQ]", act_pixel_freq);
+
+
+ stage = 16;
+ }
+
+
+ /* 14/16. Find actual horizontal frequency (kHz)
+ *
+ * [ACT H FREQ] = 1000*ACT_PIXEL_FREQ/TOTAL_PIXELS
+ */
+
+ act_h_freq = 1000 * act_pixel_freq / total_pixels;
+
+ print_value(stage, "[ACT H FREQ]", act_h_freq);
+ stage += 1;
+
+
+ /* 15/17. Find actual field rate (Hz)
+ *
+ * [ACT FIELD RATE] = 1000*ACT_H_FREQ/TOTAL_V_LINES
+ */
+
+ act_field_rate = 1000 * act_h_freq / total_v_lines;
+
+ print_value(stage, "[ACT FIELD RATE]", act_field_rate);
+ stage += 1;
+
+
+ /* 16/18. Find actual vertical frame frequency (Hz)
+ *
+ * [ACT FRAME RATE] = IF(INT_RQD?=Y,ACT_FIELD_RATE/2,ACT_FIELD_RATE)
+ */
+
+ act_frame_rate = interlace ?
+ (act_field_rate / 2) : act_field_rate;
+
+ print_value(stage, "[ACT FRAME RATE]", act_frame_rate);
+
+
+
+ /*
+ * Extra computations not numbered in the CVT spreadsheet.
+ */
+
+
+ /* 20. Find Horizontal Back Porch.
+ *
+ * [H BACK PORCH] = H_BLANK/2
+ */
+
+ h_back_porch = h_blank/2;
+
+ print_value(20, "[H BACK PORCH]", h_back_porch);
+
+
+ /* 21. Find Horizontal Front Porch.
+ *
+ * [H SYNC RND] = IF(RED_BLANK_RQD?="Y",RB_H_SYNC,(ROUNDDOWN((H_SYNC_PER/100*TOTAL_PIXELS/CELL_GRAN_RND),0))*CELL_GRAN_RND)
+ */
+
+ if (reduced_blank) {
+ h_sync_rnd = RB_H_SYNC;
+ } else {
+ h_sync_rnd = floor(H_SYNC_PER/100.0*total_pixels/CELL_GRAN_RND)
+ * CELL_GRAN_RND;
+ }
+
+ print_value(21, "[H SYNC RND]", h_sync_rnd);
+
+
+ /* 22. Find Horizontal Front Porch.
+ *
+ * [H FRONT PORCH] = H_BLANK - H_BACK_PORCH - H_SYNC_RND
+ */
+
+ h_front_porch = h_blank - h_back_porch - h_sync_rnd;
+
+ print_value(22, "[H FRONT PORCH]", h_front_porch);
+
+
+ /* 23. Find Vertical Front Porch.
+ *
+ * [V FRONT PORCH] = IF(RED_BLANK_RQD?="y",RB_V_FPORCH,MIN_V_PORCH_RND)
+ */
+
+ v_front_porch = reduced_blank ? RB_V_FPORCH : MIN_V_PORCH_RND;
+
+ print_value(23, "[V FRONT PORCH]", v_front_porch);
+
+
+
+ /* finally, pack the results in the mode struct */
+
+ m->hr = (int) (h_pixels_rnd);
+ m->hss = (int) (h_pixels_rnd + h_front_porch);
+ m->hse = (int) (h_pixels_rnd + h_front_porch + h_sync_rnd);
+ m->hfl = (int) (total_pixels);
+
+#if 0
+ m->vr = (int) (v_lines_rnd);
+ m->vss = (int) (v_lines_rnd + v_front_porch);
+ m->vse = (int) (v_lines_rnd + v_front_porch + v_sync_rnd);
+ m->vfl = (int) (total_v_lines);
+#else
+ {
+ int real_v_lines = v_lines;
+ m->vr = (int) (real_v_lines);
+ m->vss = (int) (real_v_lines + v_front_porch);
+ m->vse = (int) (real_v_lines + v_front_porch + v_sync_rnd);
+ m->vfl = (int) (total_v_lines - v_lines_rnd + real_v_lines);
+ }
+#endif
+
+ m->pclk = act_pixel_freq;
+ m->h_freq = act_h_freq;
+ m->v_freq = freq;
+ m->real_v_rate = act_field_rate;
+
+ m->in = interlaced;
+ m->rb = reduced_blank;
+
+ return (m);
+
+} // vert_refresh()
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/src/cvt.h b/src/cvt.h
new file mode 100644
index 0000000..7f2ce74
--- /dev/null
+++ b/src/cvt.h
@@ -0,0 +1,26 @@
+#ifndef __CVT_H_
+#define __CVT_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* struct definitions */
+
+typedef struct __mode
+{
+ int hr, hss, hse, hfl;
+ int vr, vss, vse, vfl;
+ float pclk, h_freq, v_freq;
+ float real_v_rate;
+ int rb, in;
+} mode;
+
+mode *vert_refresh (int h_pixels, int v_lines, float freq,
+ int interlaced, int reduced_blank, int margins);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/i18n/de.ts b/src/i18n/de.ts
index 6e4a48a..ef4f6fc 100644
--- a/src/i18n/de.ts
+++ b/src/i18n/de.ts
@@ -1,30 +1,86 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
-<TS version="2.1" language="de_DE">
+<TS version="2.1">
<context>
<name>TimeOutDialog</name>
<message>
<location filename="../timeoutdialog.cpp" line="16"/>
<source>%v seconds</source>
- <translation>Reset in %v Sekunden</translation>
+ <translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Widget</name>
<message>
- <location filename="../widget.ui" line="28"/>
+ <location filename="../widget.ui" line="29"/>
+ <source>Clone</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../widget.ui" line="35"/>
+ <source>In this mode, all connected outputs will display the same image. This is most useful if there is one monitor and one projector currently connected to the computer.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../widget.ui" line="58"/>
+ <source>Please select the mode you want to apply. The mode that has been detected as the potentially best mode is highlighted.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../widget.ui" line="86"/>
+ <location filename="../widget.ui" line="171"/>
+ <location filename="../widget.ui" line="208"/>
<source>Apply</source>
- <translation>Anwenden</translation>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../widget.ui" line="102"/>
+ <location filename="../widget.cpp" line="40"/>
+ <source>Dual Screen</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../widget.ui" line="127"/>
+ <source>&lt;- Swap -&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../widget.ui" line="181"/>
+ <source>Advanced Setup</source>
+ <translation type="unfinished"></translation>
</message>
<message>
- <location filename="../widget.cpp" line="394"/>
+ <location filename="../widget.cpp" line="145"/>
<source>Do you want to keep this resolution?</source>
- <translation>Möchten Sie diese Auflösung beibehalten?</translation>
+ <translation type="unfinished"></translation>
</message>
<message>
- <location filename="../widget.cpp" line="395"/>
+ <location filename="../widget.cpp" line="146"/>
<source>Keep</source>
- <translation>Beibehalten</translation>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>main</name>
+ <message>
+ <location filename="../main.cpp" line="70"/>
+ <source>Automatically configure modes and set up screens.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../main.cpp" line="74"/>
+ <source>Show config GUI if more than one screen is connected.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../main.cpp" line="78"/>
+ <source>Keep running in background and show GUI again when number of screens changes.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../main.cpp" line="82"/>
+ <source>Test mode, don&apos;t actually apply any changes.</source>
+ <translation type="unfinished"></translation>
</message>
</context>
</TS>
diff --git a/src/main.cpp b/src/main.cpp
index f0c2dba..1493bcd 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -1,23 +1,90 @@
-// Copyright 2013, University of Freiburg,
-// Author: Manuel Schneider <ms1144>
-
-#include <QtWidgets/QApplication> // for Qt5
#include "widget.h"
+#include "main.h"
+#include "x.h"
+
+#include <QApplication>
+#include <QLibraryInfo>
+#include <QTranslator>
+#include <QCommandLineParser>
+
+namespace {
+bool _testMode, _autoSetup, _showGui, _backgroundMode;
+}
+
+namespace CommandLine
+{
+bool testMode() { return _testMode; }
+bool autoSetup() { return _autoSetup; }
+bool showGui() { return _showGui; }
+bool backgroundMode() { return _backgroundMode; }
+}
+
+static void parseCommandLine(const QApplication &a);
int main(int argc, char *argv[])
{
- QApplication a(argc, argv);
- // System strings
- QTranslator *qtTranslator = new QTranslator(&a);
- qtTranslator->load(QLocale::system(), "qt", "_", QLibraryInfo::location(QLibraryInfo::TranslationsPath));
- a.installTranslator(qtTranslator);
- // App specific
- QTranslator *translator = new QTranslator(&a);
- translator->load(QLocale::system(), ":");
- a.installTranslator(translator);
- Widget w(argc == 2 && QString(argv[1]) == QString("--test"));
- w.show();
- return a.exec();
+ QApplication a(argc, argv);
+ QCoreApplication::setApplicationName("BeamerGUI XP - Home Edition");
+ QCoreApplication::setApplicationVersion("2.0");
+ // System strings
+ QTranslator *qtTranslator = new QTranslator(&a);
+ qtTranslator->load(QLocale::system(), "qt", "_", QLibraryInfo::location(QLibraryInfo::TranslationsPath));
+ a.installTranslator(qtTranslator);
+ // App specific
+ QTranslator *translator = new QTranslator(&a);
+ translator->load(QLocale::system(), ":");
+ a.installTranslator(translator);
+
+ parseCommandLine(a);
+
+ ScreenMode currentMode;
+ if (CommandLine::autoSetup()) {
+ currentMode = ScreenSetup::inst()->setDefaultMode(CommandLine::testMode());
+ } else {
+ currentMode = ScreenSetup::inst()->getCurrentMode();
+ }
+
+ bool showNow = (CommandLine::showGui() && currentMode != ScreenMode::Single);
+
+ Widget *w = nullptr;
+ if (CommandLine::backgroundMode() || showNow) {
+ w = new Widget();
+ if (showNow) {
+ w->show();
+ }
+ }
+ if (w == nullptr)
+ return 0;
+ return a.exec();
}
-////////////////////////////////////////////////////////////////////////////////
+static void parseCommandLine(const QApplication &a)
+{
+ // Command line
+ QCommandLineParser parser;
+ parser.setApplicationDescription("Utility for detecting and configuring screen setup");
+ parser.addHelpOption();
+ parser.addVersionOption();
+ // Option for adding modes and trying to auto-setup
+ QCommandLineOption oAutoSetup(QStringList() << "a" << "auto",
+ QCoreApplication::translate("main", "Automatically configure modes and set up screens."));
+ parser.addOption(oAutoSetup);
+ // Show config GUI if more than one screen
+ QCommandLineOption oShowGui(QStringList() << "g" << "gui",
+ QCoreApplication::translate("main", "Show config GUI if more than one screen is connected."));
+ parser.addOption(oShowGui);
+ // Keep running and detect screen setup changes
+ QCommandLineOption oBackground(QStringList() << "b" << "background",
+ QCoreApplication::translate("main", "Keep running in background and show GUI again when number of screens changes."));
+ parser.addOption(oBackground);
+ // Test mode -- pretend to do setup
+ QCommandLineOption oTest(QStringList() << "t" << "test",
+ QCoreApplication::translate("main", "Test mode, don't actually apply any changes."));
+ parser.addOption(oTest);
+ // PARSE
+ parser.process(a);
+ _testMode = parser.isSet(oTest);
+ _autoSetup = parser.isSet(oAutoSetup);
+ _showGui = parser.isSet(oShowGui);
+ _backgroundMode = parser.isSet(oBackground);
+}
diff --git a/src/main.h b/src/main.h
new file mode 100644
index 0000000..9aa2d8d
--- /dev/null
+++ b/src/main.h
@@ -0,0 +1,12 @@
+#ifndef MAIN_H
+#define MAIN_H
+
+namespace CommandLine
+{
+bool testMode();
+bool autoSetup();
+bool showGui();
+bool backgroundMode();
+}
+
+#endif // MAIN_H
diff --git a/src/widget.cpp b/src/widget.cpp
index bef88db..751c69d 100644
--- a/src/widget.cpp
+++ b/src/widget.cpp
@@ -1,383 +1,126 @@
-// Copyright 2013, University of Freiburg,
-// Author: Manuel Schneider <ms1144>
-
-#include <iostream>
-#include <algorithm>
-
-#include <QDebug>
-#include <QtWidgets/QAction>
-
#include "widget.h"
#include "ui_widget.h"
#include "timeoutdialog.h"
-#include "math.h"
+#include "x.h"
+#include "main.h"
+
+#include <QDebug>
+#include <QtWidgets/QAction>
//______________________________________________________________________________
-Widget::Widget(bool testMode, QWidget *parent) :
- QWidget(parent),
- _ui(new Ui::Widget)
+Widget::Widget(QWidget *parent) :
+ QWidget(parent),
+ _ui(new Ui::Widget)
{
- _ui->setupUi(this);
-
- // Get initial data (to be freed)
- _display = XOpenDisplay(NULL);
- _screenResources = XRRGetScreenResourcesCurrent(
- _display,
- DefaultRootWindow(_display));
-
- // Get the information about the X elements
- updateScreenResources();
-
- if (testMode) {
- while (_connectedOutputList.size() > 2) {
- _connectedOutputList.pop_front();
- }
- }
-
- switch ( _connectedOutputList.size() ){
- /*************************************************************************/
- case 1:// In case of one connected output - xrandr --auto
- qDebug() << "One connected output";
- exit(0);
- break;
- case 2: // In case of two connected outputs
-
- qDebug() << "Two connected outputs";
-
- // Check if we are in clone mode
- if (testMode || cloneMode()) {
-
- qDebug() << "Dual output with cloned screen!";
-
- // TODO make that mess nice and clean
- double_t w0 = _outputMap[_connectedOutputList[0]]->mm_width;
- double_t h0 = _outputMap[_connectedOutputList[0]]->mm_height;
-
- double_t w1 = _outputMap[_connectedOutputList[1]]->mm_width;
- double_t h1 = _outputMap[_connectedOutputList[1]]->mm_height;
-
- // Get a human readable reference
- if (w0 == 0 && h0 == 0) {
- _beamer = _connectedOutputList[0];
- _monitor = _connectedOutputList[1];
- } else if (w1 == 0 && h1 == 0) {
- _beamer = _connectedOutputList[1];
- _monitor = _connectedOutputList[0];
- } else {
- double_t d0 = sqrt((pow(w0, 2) + pow(h0, 2)));
- double_t d1 = sqrt((pow(w1, 2) + pow(h1, 2)));
-
- if (d0 > d1) {
- _beamer = _connectedOutputList[0];
- _monitor = _connectedOutputList[1];
- } else {
- _beamer = _connectedOutputList[1];
- _monitor = _connectedOutputList[0];
- }
- }
-
- // Intersect them by the resolution sorted, dont care about O(n³)
- QList<QPair<XRRModeInfo*, XRRModeInfo*> > commonModes;
-
- // Iterate over the modes the monitor supports
- for (int i = 0; i < _outputMap[_monitor]->nmode; ++i) {
-
- XRRModeInfo* monitorMode = _modeMap[_outputMap[_monitor]->modes[i]];
-
- // Skip interlace modes
- if ( monitorMode->modeFlags & RR_Interlace )
- continue;
-
- // Iterate over the modes the beamer supports
- for (int j = 0; j < _outputMap[_beamer]->nmode; ++j) {
-
- XRRModeInfo* beamerMode = _modeMap[_outputMap[_beamer]->modes[j]];
-
- // Skip interlace modes
- if ( beamerMode->modeFlags & RR_Interlace )
- continue;
-
- // Only if the modes have the same size, list them in common modes
- if ( monitorMode->height == beamerMode->height
- && monitorMode->width == beamerMode->width ) {
-
- // Build a sorted list of common modes in descending order
- QList<QPair<XRRModeInfo*, XRRModeInfo*> >::iterator k
- = commonModes.begin();
- for (;;++k) {
-
- // If at the end, the mode to insert is the smallest, insert.
- // This has to be first to avoid segfaults
- if (k == commonModes.end()) {
-
- commonModes.insert(k, qMakePair (monitorMode, beamerMode));
- break;
- }
-
- // If the mode to insert is larger than k, insert.
- if ( monitorMode->width > k->first->width) {
- commonModes.insert(k, qMakePair (monitorMode, beamerMode));
- break;
- }
-
- // If the width is the same ...
- if ( monitorMode->width == k->first->width ) {
-
- // ... and the height is the same, the mode already exists
- if ( monitorMode->height == k->first->height ) {
- break;
- }
-
- // ... and the height is larger, insert.
- if ( monitorMode->height > k->first->height ) {
- commonModes.insert(k, qMakePair (monitorMode, beamerMode));
- break;
- }
- }
- }
- }
- }
- }
-
-
- // Check if the beamer transmitted reliable data.
- bool gotEDID = false;
- int nprop;
- Atom *props = XRRListOutputProperties(_display, _beamer, &nprop);
- for (int i = 0; i < nprop; ++i) {
- char *atom_name = XGetAtomName (_display, props[i]);
- if ( strcmp (atom_name, "EDID") == 0) {
- gotEDID = true;
- XFree (atom_name);
- break;
- }
- XFree (atom_name);
- }
- free(props);
-
- // If the beamer transmits no reliable EDID data add modes
-#ifdef QT_DEBUG
- gotEDID = false;
-#endif
- if (gotEDID) {
-
- qDebug() << "GOT EDID!";
-
- // Extract the preferred mode of the beamer
- RRMode preferredBeamerModeId = 0;
- for (int i = 0; i < _outputMap[_beamer]->nmode; ++i) {
- if (i < _outputMap[_beamer]->npreferred) {
- preferredBeamerModeId = _outputMap[_beamer]->modes[i];
- break;
- }
- }
-
- // Compute the aspect ratio of the beamer
- float aspectRatio = (float)_modeMap[preferredBeamerModeId]->width
- / _modeMap[preferredBeamerModeId]->height;
-
- // Fill widget with data
- for (QList<QPair<XRRModeInfo*, XRRModeInfo*> >::iterator i
- = commonModes.begin(); i != commonModes.end(); ++i ) {
- float modeAspectRatio = ((float)i->first->width / i->first->height);
- if ( abs(modeAspectRatio - aspectRatio) < 0.05 ) // APPROX
- _ui->comboBox->addItem(i->first->name,
- QList<QVariant>()
- << QVariant((qulonglong)i->first->id)
- << QVariant((qulonglong)i->second->id));
- }
- } else {
-
- qDebug() << "NO EDID!";
-
- // Fill widget with data without AR match
- // Fill widget with data
- for ( QList<QPair<XRRModeInfo*, XRRModeInfo*> >::iterator i
- = commonModes.begin(); i != commonModes.end(); ++i ) {
- qDebug() << "Insert into QComboBox"
- << i->first->width << "x" << i->first->height
- << "(" << i->first->id << ")";
- _ui->comboBox->addItem(i->first->name,
- QList<QVariant>()
- << QVariant((qulonglong)i->first->id)
- << QVariant((qulonglong)i->second->id));
- }
- }
-
- // Set the current resolution highlighted
- QString n = _modeMap[_crtcMap[_outputMap[_monitor]->crtc]->mode]->name;
- int index = _ui->comboBox->findText(n);
- _ui->comboBox->setCurrentIndex(index);
-
- // Remove borders and stuff
- setWindowFlags(Qt::Widget | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint | Qt::X11BypassWindowManagerHint);
- //setStyleSheet("background:transparent;");
- //setAttribute(Qt::WA_TranslucentBackground);
-
- // Resize widget to its content
- resize(sizeHint());
- show();
- QTimer *timer = new QTimer(this);
- connect(timer, SIGNAL(timeout()), this, SLOT(bringToTopTimer()));
- timer->start(1000);
-
- // Center dialog on screenbottom
- const QRect desktopRect = QApplication::desktop()->screenGeometry();
- this->move( desktopRect.width()/2-this->width()/2,
- desktopRect.height()-this->height());
-
- // Connect button signal to appropriate slot
- connect(_ui->pushButton, SIGNAL(clicked()), this, SLOT(handleButton()));
- }
- /*********************************************************************/
- // If NEITHER of the outputs is a beamer (likely dualscreen setup)
- else {
- // TODO(manuel): Future feature. Setup dualscreen
- qDebug() << "Dual output with extended screen!";
- exit(0);
- }
- break;
- /*************************************************************************/
- default:
- // If there are more than 3 outputs
- // its up to the user. Quit.
- qDebug() << "More than two outputs. Quit.";
- exit(0);
- break;
- }
- /*************************************************************************/
+ _ui->setupUi(this);
+ setWindowFlags(Qt::Tool | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint);
}
void Widget::bringToTopTimer()
{
- if (_ui->comboBox->view()->isVisible()) return;
- raise();
+ auto combos = this->findChildren<QComboBox*>();
+ for (auto combo : combos) {
+ if (combo->view()->isVisible())
+ return;
+ }
+ raise();
}
//______________________________________________________________________________
-void Widget::updateScreenResources()
-{
- // Clear the modemap (nothing to be freed, stored in screenResources)
- _modeMap.clear();
-
- // Create the modemap
- qDebug() << "_MODES_";
- for (int i = 0; i < _screenResources->nmode; ++i) {
- _modeMap.insert(
- _screenResources->modes[i].id,
- &_screenResources->modes[i]);
- qDebug() << _screenResources->modes[i].id << "\t"
- << _screenResources->modes[i].width << "x"
- << _screenResources->modes[i].height;
- }
-
- // Clear the crtmap
- for (CrtcMap::iterator it = _crtcMap.begin();
- it != _crtcMap.end();) {
- XRRFreeCrtcInfo(*it);
- it = _crtcMap.erase(it);
- }
-
- // Create crtcMap
- for (int i = 0; i < _screenResources->ncrtc; ++i) {
- XRRCrtcInfo * info = XRRGetCrtcInfo(
- _display,
- _screenResources,
- _screenResources->crtcs[i]);
- _crtcMap.insert(
- _screenResources->crtcs[i],
- info);
- }
-
- // Clear the outputmap
- for (OutputMap::iterator it = _outputMap.begin();
- it != _outputMap.end();) {
- XRRFreeOutputInfo(*it);
- it = _outputMap.erase(it);
- }
-
- // Create outputmap and connectedOutputMap
- for (int i = 0; i < _screenResources->noutput; ++i) {
- XRROutputInfo* info = XRRGetOutputInfo(
- _display,
- _screenResources,
- _screenResources->outputs[i]);
- _outputMap.insert(
- _screenResources->outputs[i],
- info);
+Widget::~Widget() {
- // Store the connected ones separate
- if ( info->connection == RR_Connected )
- _connectedOutputList.push_back(_screenResources->outputs[i]);
- }
}
-
-//______________________________________________________________________________
-Widget::~Widget() {
- delete _ui;
- XCloseDisplay(_display);
- XRRFreeScreenResources(_screenResources);
+void Widget::showEvent(QShowEvent *event)
+{
+ QWidget::showEvent(event);
+ ScreenMode mode = ScreenSetup::inst()->getCurrentMode();
+ if (ScreenSetup::inst()->getOutputCount() == 2 || mode == ScreenMode::Dual) {
+ if (_ui->tabWidget->widget(1) != _ui->tabDual) {
+ _ui->tabWidget->insertTab(1, _ui->tabDual, tr("Dual Screen"));
+ }
+ } else {
+ if (_ui->tabWidget->widget(1) == _ui->tabDual) {
+ _ui->tabWidget->removeTab(1);
+ }
+ }
+ switch (mode) {
+ case ScreenMode::Single:
+ case ScreenMode::Clone:
+ _ui->tabWidget->setCurrentWidget(_ui->tabClone);
+ break;
+ case ScreenMode::Dual:
+ _ui->tabWidget->setCurrentWidget(_ui->tabDual);
+ break;
+ case ScreenMode::Advanced:
+ _ui->tabWidget->setCurrentWidget(_ui->tabAdvanced);
+ break;
+ }
+ initControls();
}
+void Widget::initControls()
+{
+ auto modes = ScreenSetup::inst()->getCommonModes();
+ for (auto mode : modes) {
+ _ui->cboCloneResolution->addItem(QString::asprintf("%ux%u", mode.first, mode.second));
+ }
+ // Clear advanced controls
+ auto widgets = _ui->advancedCombos->findChildren<QWidget*>();
+ for (auto w : widgets) {
+ _ui->advancedCombos->removeWidget(w);
+ w->deleteLater();
+ }
+ // Create new
+ QHash<QString, int> positions = ScreenSetup::inst()->getScreenPositions();
+ ScreenSetup::ResolutionVector resolutions = ScreenSetup::inst()->getVirtualResolutions();
+ int row = 0;
+ for (auto it = positions.begin(); it != positions.end(); ++it) {
+ _ui->advancedCombos->addWidget(new QLabel(it.key()), row, 0, 1, -1);
+ _ui->advancedCombos->addWidget(new QComboBox(), row, 2);
+ row++;
+ }
+}
//______________________________________________________________________________
-void Widget::handleButton(){
+void Widget::handleButton() {
+ // Apply
- /*************************** Backup the crtcinfos ***************************/
-
- qDebug() << "Backup the crtc infos";
-
- CrtcMap backup;
- for ( CrtcMap::iterator it = _crtcMap.begin();
- it != _crtcMap.end(); ++it ) {
- backup[it.key()] = new XRRCrtcInfo;
- backup[it.key()]->x = it.value()->x;
- backup[it.key()]->y = it.value()->y;
- backup[it.key()]->mode = it.value()->mode;
- backup[it.key()]->rotation = it.value()->rotation;
- backup[it.key()]->noutput = it.value()->noutput;
- backup[it.key()]->outputs = new RROutput[it.value()->noutput];
- for (int i = 0; i < it.value()->noutput; ++i) {
- backup[it.key()]->outputs[i] = it.value()->outputs[i];
- }
- }
-
- /**************************** Apply the resolution **************************/
+ /*
// Get the modes which has to be applied from QComboBox
QList<QVariant> modes =
- _ui->comboBox->itemData( _ui->comboBox->currentIndex()).toList();
+ _ui->comboBox->itemData( _ui->comboBox->currentIndex()).toList();
XRRModeInfo* monitorMode = _modeMap[modes[0].toULongLong()];
XRRModeInfo* beamerMode = _modeMap[modes[1].toULongLong()];
// First disconnect all crts to avoid conflicts
for(CrtcMap::iterator it = _crtcMap.begin(); it != _crtcMap.end(); ++it) {
Status st = XRRSetCrtcConfig(_display, _screenResources, it.key(), CurrentTime,
- 0, 0, None, RR_Rotate_0, NULL, 0);
+ 0, 0, None, RR_Rotate_0, nullptr, 0);
qDebug() << "Disconnecting" << it.key() << ":" << st;
}
// Set screensize
XRRSetScreenSize(_display, DefaultRootWindow(_display),
- monitorMode->width, monitorMode->height,
- 25.4 * monitorMode->width / 96, // standard dpi that X uses
- 25.4 * monitorMode->height / 96); // standard dpi that X uses
+ monitorMode->width, monitorMode->height,
+ 25.4 * monitorMode->width / 96, // standard dpi that X uses
+ 25.4 * monitorMode->height / 96); // standard dpi that X uses
// Apply the modes
Status stMon = XRRSetCrtcConfig(_display,
_screenResources,
- _outputMap[_monitor]->crtc,
- CurrentTime,
- 0, 0, monitorMode->id,
- RR_Rotate_0,
- &_monitor, 1);
+ _outputMap[_monitor]->crtc,
+ CurrentTime,
+ 0, 0, monitorMode->id,
+ RR_Rotate_0,
+ &_monitor, 1);
Status stBem = XRRSetCrtcConfig(_display,
- _screenResources,
- _outputMap[_beamer]->crtc,
- CurrentTime,
- 0, 0, beamerMode->id,
- RR_Rotate_0,
+ _screenResources,
+ _outputMap[_beamer]->crtc,
+ CurrentTime,
+ 0, 0, beamerMode->id,
+ RR_Rotate_0,
&_beamer, 1);
// Sync... whatever...
@@ -391,112 +134,93 @@ void Widget::handleButton(){
// Set the mouse pointer in the middle of the screen
QCursor::setPos(monitorMode->width/2, monitorMode->height/2);
- /*************************** ASK for confirmtion ****************************/
+ */
+
+ /*************************** ASK for confirmtion ****************************/
- // Show a dialog asking if the res should be kept
- TimeOutDialog keepDialog(15, this);
- keepDialog.setWindowModality(Qt::ApplicationModal);
- keepDialog.setWindowTitle(" ");
- keepDialog.setLabelText(trUtf8("Do you want to keep this resolution?"));
- keepDialog.setCancelButtonText(trUtf8("Keep"));
+ // Show a dialog asking if the res should be kept
+ TimeOutDialog keepDialog(15, this);
+ keepDialog.setWindowModality(Qt::ApplicationModal);
+ keepDialog.setWindowTitle(" ");
+ keepDialog.setLabelText(trUtf8("Do you want to keep this resolution?"));
+ keepDialog.setCancelButtonText(trUtf8("Keep"));
+ /*
keepDialog.move(monitorMode->width/2 - this->width()/2,
- monitorMode->height/2 - this->height());
- keepDialog.show();
+ monitorMode->height/2 - this->height());
+ */
+ keepDialog.show();
- while (keepDialog.isActive()) {
- QCoreApplication::processEvents();
- }
+ while (keepDialog.isActive()) {
+ QCoreApplication::processEvents();
+ }
+ /*
// If the dialog was not canceled revert the resolution
if ( !keepDialog.wasCanceled()) {
qDebug() << "User did not cancel timeout, reverting!";
- /**************************** Apply the backup ****************************/
-
- // First disconnect all crts to avoid conflicts
- for(CrtcMap::iterator it = _crtcMap.begin(); it != _crtcMap.end(); ++it) {
- XRRSetCrtcConfig(_display, _screenResources, it.key(), CurrentTime,
- 0, 0, None, RR_Rotate_0, NULL, 0);
- }
-
- // Then calc backed up screensize
- QSize ScreenSize(0,0);
- for ( CrtcMap::iterator it = backup.begin();
- it != backup.end(); ++it ) {
- // Dangerzone. Only access existing modes!
- if ( it.value()->mode != None ) {
- ScreenSize.setWidth(
- std::max((uint)ScreenSize.width(),
- it.value()->x+_modeMap[it.value()->mode]->width));
- ScreenSize.setHeight(
- std::max((uint)ScreenSize.height(),
- it.value()->y+_modeMap[it.value()->mode]->height));
- }
- }
-
- // Set screensize
- XRRSetScreenSize(_display, DefaultRootWindow(_display),
- ScreenSize.width(), ScreenSize.height(),
- 25.4 * ScreenSize.width() / 96, // dpi used by X
- 25.4 * ScreenSize.height() / 96); // dpi used by X
-
- // Apply the backup
- for ( CrtcMap::iterator it = backup.begin();
- it != backup.end(); ++it ) {
- XRRSetCrtcConfig(_display,
- _screenResources,
- it.key(),
- CurrentTime,
- it.value()->x,
- it.value()->y,
- it.value()->mode,
- it.value()->rotation,
- it.value()->outputs,
- it.value()->noutput);
- }
-
- // Sync... whatever...
- XSync (_display, False);
-
- // center dialog on screenbottom
- qDebug() << "Again center dialog on screenbottom";
- this->move( ScreenSize.width()/2 - this->width()/2,
- ScreenSize.height() - this->height());
-
- // Set the mouse pointer in the middle of the screen
- QCursor::setPos(monitorMode->width/2, monitorMode->height/2);
+ // First disconnect all crts to avoid conflicts
+ for(CrtcMap::iterator it = _crtcMap.begin(); it != _crtcMap.end(); ++it) {
+ XRRSetCrtcConfig(_display, _screenResources, it.key(), CurrentTime,
+ 0, 0, None, RR_Rotate_0, nullptr, 0);
+ }
+
+ // Then calc backed up screensize
+ QSize ScreenSize(0,0);
+ for ( CrtcMap::iterator it = backup.begin();
+ it != backup.end(); ++it ) {
+ // Dangerzone. Only access existing modes!
+ if ( it.value()->mode != None ) {
+ ScreenSize.setWidth(
+ std::max((uint)ScreenSize.width(),
+ it.value()->x+_modeMap[it.value()->mode]->width));
+ ScreenSize.setHeight(
+ std::max((uint)ScreenSize.height(),
+ it.value()->y+_modeMap[it.value()->mode]->height));
+ }
+ }
+
+ // Set screensize
+ XRRSetScreenSize(_display, DefaultRootWindow(_display),
+ ScreenSize.width(), ScreenSize.height(),
+ 25.4 * ScreenSize.width() / 96, // dpi used by X
+ 25.4 * ScreenSize.height() / 96); // dpi used by X
+
+ // Apply the backup
+ for ( CrtcMap::iterator it = backup.begin();
+ it != backup.end(); ++it ) {
+ XRRSetCrtcConfig(_display,
+ _screenResources,
+ it.key(),
+ CurrentTime,
+ it.value()->x,
+ it.value()->y,
+ it.value()->mode,
+ it.value()->rotation,
+ it.value()->outputs,
+ it.value()->noutput);
+ }
+
+ // Sync... whatever...
+ XSync (_display, False);
+
+ // center dialog on screenbottom
+ qDebug() << "Again center dialog on screenbottom";
+ this->move( ScreenSize.width()/2 - this->width()/2,
+ ScreenSize.height() - this->height());
+
+ // Set the mouse pointer in the middle of the screen
+ QCursor::setPos(monitorMode->width/2, monitorMode->height/2);
}
// Delete the backup
for ( CrtcMap::iterator it = backup.begin();
- it != backup.end(); ++it ) {
- delete[] it.value()->outputs;
- delete it.value();
+ it != backup.end(); ++it ) {
+ delete[] it.value()->outputs;
+ delete it.value();
}
- // Intenal settings changed. Update!
- updateScreenResources();
+ */
}
-bool Widget::cloneMode()
-{
- bool cloneMode = true;
-
- for (CrtcMap::iterator it = _crtcMap.begin(); it != _crtcMap.end(); it++) {
- XRRCrtcInfo* crtc = it.value();
-
- // check if x starts on upper left corner
- if (crtc->x != 0 || crtc->y != 0) {
- cloneMode = false;
- }
-
- qDebug() << "width: " << crtc->width
- << "height: " << crtc->height
- << "x: " << crtc->x
- << "y: " << crtc->y
- << "mode: " << crtc->mode;
- }
-
- return cloneMode;
-}
////////////////////////////////////////////////////////////////////////////////
diff --git a/src/widget.h b/src/widget.h
index 6e8d7d6..05e27a6 100644
--- a/src/widget.h
+++ b/src/widget.h
@@ -1,57 +1,32 @@
-// Copyright 2013, University of Freiburg,
-// Author: Manuel Schneider <ms1144>
-
#ifndef WIDGET_H
#define WIDGET_H
-#include <QtWidgets> // for Qt5
+#include <QWidget> // for Qt5
#include <QDebug>
-#include <X11/Xlib.h>
-#include <X11/extensions/Xrandr.h>
-
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
- typedef QSet<RRMode> ModeSet;
- typedef QSet<RRCrtc> CrtcSet;
- typedef QSet<RROutput> OutputSet;
-
- typedef QList<RRMode> ModeList;
- typedef QList<RRCrtc> CrtcList;
- typedef QList<RROutput> OutputList;
+ Q_OBJECT
- typedef QMap<RRMode, XRRModeInfo*> ModeMap;
- typedef QMap<RRCrtc, XRRCrtcInfo*> CrtcMap;
- typedef QMap<RROutput, XRROutputInfo*> OutputMap;
-
- Q_OBJECT
-
public:
+ explicit Widget(QWidget *parent = nullptr);
+ ~Widget();
- explicit Widget(bool testMode, QWidget *parent = 0);
- ~Widget();
+protected:
+ virtual void showEvent(QShowEvent *event);
- private slots:
- void handleButton();
- void bringToTopTimer();
+private slots:
+ void handleButton();
+ void bringToTopTimer();
private:
- void timeout();
- void updateScreenResources();
- bool cloneMode();
-
- Ui::Widget * _ui;
- Display* _display;
- XRRScreenResources* _screenResources;
- ModeMap _modeMap;
- CrtcMap _crtcMap;
- OutputMap _outputMap;
- OutputList _connectedOutputList;
- RROutput _beamer, _monitor;
+ Ui::Widget *_ui;
+
+ void initControls();
};
#endif // WIDGET_H
diff --git a/src/widget.ui b/src/widget.ui
index 6086eae..2287936 100644
--- a/src/widget.ui
+++ b/src/widget.ui
@@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
- <width>600</width>
- <height>480</height>
+ <width>796</width>
+ <height>309</height>
</rect>
</property>
<property name="sizePolicy">
@@ -16,17 +16,203 @@
<verstretch>0</verstretch>
</sizepolicy>
</property>
- <layout class="QGridLayout" name="gridLayout">
- <item row="0" column="0">
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
- <widget class="QComboBox" name="comboBox"/>
- </item>
- <item>
- <widget class="QPushButton" name="pushButton">
- <property name="text">
- <string>Apply</string>
+ <widget class="QTabWidget" name="tabWidget">
+ <property name="currentIndex">
+ <number>2</number>
</property>
+ <widget class="QWidget" name="tabClone">
+ <attribute name="title">
+ <string>Clone</string>
+ </attribute>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>In this mode, all connected outputs will display the same image. This is most useful if there is one monitor and one projector currently connected to the computer.</string>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>Please select the mode you want to apply. The mode that has been detected as the potentially best mode is highlighted.</string>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="cboCloneResolution"/>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_3">
+ <item>
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QPushButton" name="btnCloneApply">
+ <property name="text">
+ <string>Apply</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="tabDual">
+ <property name="styleSheet">
+ <string notr="true">#lblDualLeft, #lblDualRight {
+border-radius: 3px;
+border: 2px solid black;
+}</string>
+ </property>
+ <attribute name="title">
+ <string>Dual Screen</string>
+ </attribute>
+ <layout class="QVBoxLayout" name="verticalLayout4" stretch="1,0">
+ <item>
+ <layout class="QHBoxLayout" name="dualContainer">
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <item>
+ <widget class="QLabel" name="lblDualLeft">
+ <property name="text">
+ <string/>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="cboDualLeft"/>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QPushButton" name="btnDualSwap">
+ <property name="text">
+ <string>&lt;- Swap -&gt;</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout_3">
+ <item>
+ <widget class="QLabel" name="lblDualRight">
+ <property name="text">
+ <string/>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="cboDualRight"/>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_4">
+ <item>
+ <spacer name="horizontalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QPushButton" name="btnDualApply">
+ <property name="text">
+ <string>Apply</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="tabAdvanced">
+ <attribute name="title">
+ <string>Advanced Setup</string>
+ </attribute>
+ <layout class="QVBoxLayout" name="verticalLayout9" stretch="1,0,0">
+ <item>
+ <layout class="QHBoxLayout" name="advancedContainer"/>
+ </item>
+ <item>
+ <layout class="QGridLayout" name="advancedCombos"/>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="advancedButtons">
+ <item>
+ <spacer name="horizontalSpacer_3">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QPushButton" name="btnAdvancedApply">
+ <property name="text">
+ <string>Apply</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
</widget>
</item>
</layout>
diff --git a/src/x.cpp b/src/x.cpp
index e0aa12f..74020e0 100644
--- a/src/x.cpp
+++ b/src/x.cpp
@@ -1,513 +1,621 @@
#include "x.h"
+#include "cvt.h"
#include <QDebug>
-namespace X
-{
+
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
- Screen * Screen::_instance = NULL;
-
- Screen::Screen()
- {
- // Get initial data (to be freed)
- _display = XOpenDisplay(NULL);
- _screenResources = XRRGetScreenResourcesCurrent(_display, DefaultRootWindow(_display));
-
- /* Get informations about Xserver */
- updateScreenResources();
-
- }
-
- //___________________________________________________________________________
- void Screen::updateScreenResources()
- {
- // Create the modemap
- _modeMap.clear();
- for (int i = 0; i < _screenResources->nmode; ++i)
- {
- _modeMap.insert(
- _screenResources->modes[i].id,
- Mode(&_screenResources->modes[i])
- );
- }
-
- // Create crtcMap
- _crtcMap.clear();
- for (int i = 0; i < _screenResources->ncrtc; ++i)
- {
- _crtcMap.insert(
- _screenResources->crtcs[i],
- Crtc(_screenResources->crtcs[i],this)
- );
- }
-
- // Create outputMap
- _outputMap.clear();
- for (int i = 0; i < _screenResources->noutput; ++i)
- {
- _outputMap.insert(
- _screenResources->outputs[i],
- new Output(_screenResources->outputs[i], this)
- );
- }
-
- // Create connectedOutputMap
- _connectedOutputList.clear();
- for (OutputMap::iterator it = _outputMap.begin();
- it != _outputMap.end(); ++it)
- if ( (*it)->isConnected() )
- _connectedOutputList.push_back((*it)->_id);
- }
-
- //___________________________________________________________________________
- void Screen::createMode(
- unsigned int resX,
- unsigned int resY,
- unsigned long dotClock,
- unsigned int hSyncStart,
- unsigned int hSyncEnd,
- unsigned int hTotal,
- unsigned int vSyncStart,
- unsigned int vSyncEnd,
- unsigned int vTotal,
- QString name)
- {
- XRRModeInfo m;
- m.width = resX;
- m.height = resY;
- m.dotClock = dotClock;
- m.hSyncStart= hSyncStart;
- m.hSyncEnd = hSyncEnd;
- m.hTotal = hTotal;
- m.hSkew = 0;
- m.vSyncStart= vSyncStart;
- m.vSyncEnd = vSyncEnd;
- m.vTotal = vTotal;
- QByteArray ba = name.toLocal8Bit();
- m.name = ba.data();
-
- XRRCreateMode(const_cast<Display*>(X::Screen::inst()->display()), DefaultRootWindow(X::Screen::inst()->display()), &m);
- //XRRAddOutputMode();
- }
-
- //___________________________________________________________________________
- Screen::~Screen()
- {
- XCloseDisplay(_display);
- XRRFreeScreenResources(_screenResources);
- for (int i = 0; i < _outputMap.size(); ++i)
- delete _outputMap[i];
- }
-
-
-// //_________________________________________________________________________
-//// int Screen::applyChanges()
-//// {
-//// // First make backup to restore in case of an error or user interaction
-
-//// // Calculate screensize
-//// QRect screenSize;
-//// for (CrtcMap::iterator i = _crtcMap.begin(); i != _crtcMap.end(); ++i)
-//// screenSize = screenSize.united(i->getRect());
-
-//// /* values from xrandr */
-//// float dpi = (25.4 * DisplayHeight(_display, 0)) /
-//// DisplayHeightMM(_display, 0);
-//// int widthMM = (int) ((25.4 * screenSize.width()) / dpi);
-//// int heightMM = (int) ((25.4 * screenSize.height()) / dpi);
-
-//// // Set screensize
-//// XRRSetScreenSize(_display, DefaultRootWindow(_display),
-//// screenSize.width(),
-//// screenSize.height(),
-//// widthMM, heightMM);
-
-//// // Apply changes of each crtc
-//// // Stupid applying here, sanitychecks have to be done before
-//// for (CrtcMap::iterator i = _crtcMap.begin(); i != _crtcMap.end(); ++i)
-//// {
-//// if ( i->applyChanges() )
-//// {
-//// revertChanges();
-//// return EXIT_FAILURE;
-//// }
-//// }
-//// return EXIT_SUCCESS;
-//// }
-
-
-//// //_________________________________________________________________________
-//// void Screen::revertChanges()
-//// {
-//// // TODO
-//// }
-
-//// OutputList Screen::getConnectedOutputs() const
-//// {
-//// OutputList result;
-//// for (OutputMap::const_iterator it = _outputMap.begin();
-//// it != _outputMap.end(); ++it)
-//// if (it->isConnected())
-//// result.push_back(it->_id);
-//// return result;
-//// }
-
-/////////////////////////////////////////////////////////////////////////////////
-/////////////////////////////////////////////////////////////////////////////////
-/////////////////////////////////////////////////////////////////////////////////
-
- XElement::XElement(XID xid)
- : _id(xid), _validity(false) {}
-
-/////////////////////////////////////////////////////////////////////////////////
-/////////////////////////////////////////////////////////////////////////////////
-/////////////////////////////////////////////////////////////////////////////////
-
- Mode::Mode(XRRModeInfo *info)
- {
- if ( info == NULL )
- return;
- _id = info->id;
- _dotClock = info->dotClock;
- _hSyncStart = info->hSyncStart;
- _hSyncEnd = info->hSyncEnd;
- _hTotal = info->hTotal;
- _hSkew = info->hSkew;
- _vSyncStart = info->vSyncStart;
- _vSyncEnd = info->vSyncEnd;
- _vTotal = info->vTotal;
- _name = QString(info->name);
- _modeFlags = info->modeFlags;
- _resolution.setWidth(info->width);
- _resolution.setHeight(info->height);
- _validity = true;
- // rate = ((float) info->dotClock / ((float) info->hTotal * (float) info->vTotal));
-
- qDebug() << "Mode: " << _id << _resolution.width() << _resolution.height();
- }
-
-/////////////////////////////////////////////////////////////////////////////////
-/////////////////////////////////////////////////////////////////////////////////
-/////////////////////////////////////////////////////////////////////////////////
-
- Crtc::Crtc(XID id, Screen * parent)
- : XElement(id), _parent(parent)
- {
- // Get the information from XRROutputInfo
- XRRCrtcInfo * info = XRRGetCrtcInfo(
- const_cast<Display*>(_parent->display()),
- const_cast<XRRScreenResources*>(_parent->screenResources()),
- id);
-
- // Leave invalid if XID not existent
- if ( !info ){
- return;
- }
-
- _timestamp = info->timestamp;
- _crtcRect = QRect(
- info->x,
- info->y,
- info->width,
- info->height);
- _mode = info->mode;
-
- for (int i = 0; i < info->noutput; ++i)
- _outputs.append(info->outputs[i]);
-
- for (int i = 0; i < info->npossible; ++i)
- _possible.append(info->possible[i]);
-
- _rotation = info->rotation;
- _rotations = info->rotations;
- _validity = true;
- XRRFreeCrtcInfo(info);
-
- qDebug() << "Crtc: " << _id << _mode << _outputs
- << _crtcRect;
- }
-
-//// int Crtc::applyChanges()
-//// {
-//// return XRRSetCrtcConfig(_parent->display,
-//// _parent->screenResources,
-//// _id,
-//// _timestamp,
-//// _crtcRect.x(),
-//// _crtcRect.y(),
-//// _mode,
-//// _rotation,
-//// _outputs.toVector().data(),
-//// _outputs.size()
-//// );
-//// }
-
-/////////////////////////////////////////////////////////////////////////////////
-/////////////////////////////////////////////////////////////////////////////////
-/////////////////////////////////////////////////////////////////////////////////
-
- Output::Output(XID id, Screen * parent)
- : XElement(id), _parent(parent), _hasReliableEDID(false)
- {
- // Get the information from XRROutputInfo
- XRROutputInfo* info = XRRGetOutputInfo(
- const_cast<Display*>(_parent->display()),
- const_cast<XRRScreenResources*>(_parent->screenResources()),
- id);
-
- // Leave invalid if XID not existent
- if ( !info ){
- return;
- }
-
- _timestamp = info->timestamp;
- _crtc = info->crtc;
- _name = QString(info->name);
- _metricDimension.setWidth(info->mm_width);
- _metricDimension.setHeight(info->mm_height);
- _connection = (State)info->connection;
- //subpixel_order = info->subpixel_order;
- for (int i = 0; i < info->ncrtc; ++i)
- _crtcs.append(info->crtcs[i]);
- for (int i = 0; i < info->nclone; ++i)
- _clones.append(info->clones[i]);
-
- // List the supported modes and extract preferred
- // This is the point where creating the modemap before the outputmap
- // gets a contition.
- for (int i = 0; i < info->nmode; ++i){
- _modes.insert(info->modes[i]);
- if (i < info->npreferred)
- _preferred = i;
- }
- XRRFreeOutputInfo(info);
-
- // Check if this is a beamer
- _isProjector = _metricDimension.isEmpty();
-
- // Maybe obsolete, since no preferred mode means no EDID.
- // // EDID = ?;
- int nprop;
- Atom *props = XRRListOutputProperties(
- const_cast<Display*>(_parent->display()),
- _id,
- &nprop);
-
- for (int i = 0; i < nprop; ++i)
- {
- char *atom_name = XGetAtomName (
- const_cast<Display*>(_parent->display()),
- props[i]);
- if ( strcmp (atom_name, "EDID") == 0)
- {
-// // Print Stuff
-// unsigned long nitems, bytes_after;
-// unsigned char *prop;
-// int actual_format;
-// Atom actual_type;
-// int bytes_per_item;
-
-// XRRGetOutputProperty (dpy, ID, props[i],
-// 0, 100, False, False,
-// AnyPropertyType,
-// &actual_type, &actual_format,
-// &nitems, &bytes_after, &prop);
-
-// XRRPropertyInfo *propinfo = XRRQueryOutputProperty(dpy, ID, props[i]);
-// bytes_per_item = actual_format / 8;
-
-// fprintf (stderr, "\t%s: ", atom_name);
-// for (unsigned int k = 0; k < nitems; k++)
-// {
-// if (k != 0)
-// {
-// if ((k % 16) == 0)
-// {
-// fprintf (stderr, "\n\t\t");
-// }
-// }
-// const uint8_t *val = prop + (k * bytes_per_item);
-// fprintf (stderr, "%d02", *val);
-// }
-// free(propinfo);
- _hasReliableEDID = true;
- }
- }
- free(props);
-
-
- qDebug() << "Output: " << _id << _name << _crtc
- << _metricDimension << _clones << _modes;
- }
-
-// //_________________________________________________________________________
-//// int Screen::Output::changeMode(XID mode)
-//// {
-//// // Check if this mode is supported
-//// if ( ! _modes.contains(mode) )
-//// return 1;
-
-//// // Check if this output is connected
-//// if ( _connection != Output::Connected )
-//// return 2;
-
-//// // If this output is NOT conected to a crtc
-//// if ( ! isActive() )
-//// {
-//// // Try to find a unconnected crtc which
-//// // this output can be connected to
-//// for (CrtcList::iterator i = _crtcs.begin(); ; ++i)
-//// {
-//// // If this search reches end no appropriate
-//// // crtc has been found
-//// if ( i == _crtcs.end() )
-//// return 3;
-
-//// // If a free crtc was found connect and apply mode
-//// if ( _parent->_crtcMap[*i].getConnectedOutputs().empty() )
-//// {
-//// _parent->_crtcMap[*i].connect(this, mode);
-//// break;
-//// }
-//// }
-//// }
-//// //If it is already connected apply mode to the crtc
-//// // TODO(Manuel):continue
-//// return 0;
-//// }
-
-//// //_________________________________________________________________________
-//// int Output::changePos()
-//// {
-//// return 1;
-//// }
-/////////////////////////////////////////////////////////////////////////////////
-/////////////////////////////////////////////////////////////////////////////////
-/////////////////////////////////////////////////////////////////////////////////
-}
-
-
-
-
-
-
+enum class Projector {
+ No,
+ Yes,
+ Maybe
+};
+
+struct OutputInfo {
+ OutputInfo(RROutput id, XRROutputInfo *output, XRRCrtcInfo *crtc, XRRModeInfo *mode)
+ : output(output), crtc(crtc), mode(mode), id(id), position(-1), isProjector(Projector::No) {}
+ ~OutputInfo() {
+ XRRFreeOutputInfo(output);
+ }
+ XRROutputInfo* output;
+ XRRCrtcInfo* crtc;
+ XRRModeInfo* mode;
+ RROutput id;
+ QString modelName;
+ QString outputName;
+ int position;
+ Projector isProjector;
+};
+
+ScreenSetup * ScreenSetup::_instance = nullptr;
+
+ScreenSetup::ScreenSetup() : _screenResources(nullptr)
+{
+ // Get initial data (to be freed)
+ _display = XOpenDisplay(nullptr);
+ if (_display == nullptr) {
+ qFatal("Cannot open display");
+ ::exit(1);
+ }
+ _EDID_ATOM = XInternAtom(_display, RR_PROPERTY_RANDR_EDID, False);
+ /* Get informations about Xserver */
+ updateScreenResources();
+}
+void ScreenSetup::freeResources()
+{
+ // Clear the modemap (nothing to be freed, stored in screenResources)
+ _modeMap.clear();
+ _resolutions.clear();
+
+ // Clear output info
+ for (OutputInfo *output : _outputMap.values()) {
+ delete output;
+ }
+ _outputMap.clear();
+
+ // Clear crtc info
+ for (XRRCrtcInfo *info : _crtcMap.values()) {
+ XRRFreeCrtcInfo(info);
+ }
+ _crtcMap.clear();
+
+ // Release resources
+ if (_screenResources != nullptr) {
+ XRRFreeScreenResources(_screenResources);
+ _screenResources = nullptr;
+ }
+ XRRFreeScreenResources(_screenResources);
+}
+static bool xRectLessThan(const OutputInfo* const &a, const OutputInfo* const &b)
+{
+ return a->crtc->x < b->crtc->x;
+}
+static double toVertRefresh(const XRRModeInfo *mode)
+{
+ if (mode->hTotal > 0 && mode->vTotal > 0)
+ return (double(mode->dotClock) / (double(mode->hTotal) * double(mode->vTotal)));
+ return 0;
+}
+/**
+ * Check list of known model names that falsely report a screen size or similar
+ */
+static QStringList initProjectorList()
+{
+ QStringList list;
+ list << "AT-HDVS-RX"; // Switchbox
+ // TODO: Load from file
+ return list;
+}
+static bool isProjectorName(const QString &name)
+{
+ static QStringList projectors = initProjectorList();
+ return projectors.contains(name);
+}
+//___________________________________________________________________________
+void ScreenSetup::updateScreenResources()
+{
+ freeResources();
+ _screenResources = XRRGetScreenResourcesCurrent(_display, DefaultRootWindow(_display));
+ // Create the modemap
+ qDebug() << "Modes";
+ for (int i = 0; i < _screenResources->nmode; ++i) {
+ _modeMap.insert(
+ _screenResources->modes[i].id,
+ &_screenResources->modes[i]);
+ qDebug() << _screenResources->modes[i].id << "\t"
+ << _screenResources->modes[i].width << "x"
+ << _screenResources->modes[i].height;
+ }
+
+ /*
+ int num = 0;
+ XRRMonitorInfo *mi = XRRGetMonitors(_display, DefaultRootWindow(_display), True, &num);
+ for (int i = 0; i < num; ++i) {
+ char *name = XGetAtomName(_display, mi [i].name);
+ if (name == nullptr)
+ continue;
+ qDebug() << "some monitor with name" << name << mi[i].primary;
+ XFree(name);
+ }
+ */
+
+ // Create crtcMap
+ qDebug() << "CRTCs";
+ for (int i = 0; i < _screenResources->ncrtc; ++i) {
+ XRRCrtcInfo * info = XRRGetCrtcInfo(
+ _display,
+ _screenResources,
+ _screenResources->crtcs[i]);
+ if (info == nullptr) {
+ qDebug() << "Error getting CRTC info for crtc" << i;
+ continue;
+ }
+ qDebug() << _screenResources->crtcs[i] << "-- Outputs:" << info->noutput << "Possible:" << info->npossible;
+ _crtcMap.insert(_screenResources->crtcs[i], info);
+ }
+
+ // Create outputmap and connectedOutputMap
+ qDebug() << "Outputs";
+ QHash<RROutput, QString> tempMap;
+ QMap<Projector, int> typeCount;
+ for (int i = 0; i < _screenResources->noutput; ++i) {
+ XRROutputInfo* info = XRRGetOutputInfo(
+ _display,
+ _screenResources,
+ _screenResources->outputs[i]);
+ if (info == nullptr) {
+ qDebug() << "Error getting info for output" << i;
+ continue;
+ }
+ const QString outputName = QString::fromLocal8Bit(info->name, info->nameLen);
+ tempMap.insert(_screenResources->outputs[i], outputName);
+ if (info->connection != RR_Connected) {
+ qDebug() << "Ignoring disconnected output" << outputName;
+ XRRFreeOutputInfo(info);
+ continue;
+ }
+ if (!_crtcMap.contains(info->crtc)) {
+ qDebug() << "Have output" << outputName << "with no known crtc";
+ XRRFreeOutputInfo(info);
+ continue;
+ }
+ XRRCrtcInfo *crtc = _crtcMap.value(info->crtc);
+ if (!_modeMap.contains(crtc->mode)) {
+ qDebug() << "Have output" << outputName << " with crtc with no known mode";
+ XRRFreeOutputInfo(info);
+ continue;
+ }
+ XRRModeInfo *mode = _modeMap.value(crtc->mode);
+ OutputInfo *oi = new OutputInfo(_screenResources->outputs[i], info, crtc, mode);
+ oi->outputName = outputName;
+ if (!this->readEdid(oi)) { // We have no EDID - take it as an indicator that there's a dumb output split/switch box
+ oi->isProjector = Projector::Maybe;
+ } else if ((info->mm_height == 0 && info->mm_width == 0) || isProjectorName(oi->modelName)) {
+ oi->isProjector = Projector::Yes; // Screens with size 0x0 are projectors by convention
+ } else if (info->mm_width > 500 && info->mm_height > 500) { // Big screen - probably should be handled like a projector
+ oi->isProjector = Projector::Yes;
+ } else if (info->mm_width > 350 && info->mm_height > 350) { // Somewhere in-between
+ oi->isProjector = Projector::Maybe;
+ }
+ typeCount[oi->isProjector]++;
+ _outputMap.insert(_screenResources->outputs[i], oi);
+ qDebug() << "Connected" << outputName << "-- Clones:" << info->nclone << "Modes:" << info->nmode << "Crtcs:" << info->ncrtc << "Preferred:" << info->npreferred;
+ }
+ // Final checks for projector
+ if (typeCount[Projector::Yes] == 0) {
+ // No definitive projector
+ if (typeCount[Projector::Maybe] > 0 && typeCount[Projector::No] > 0) {
+ // Potential projector(s) and normal screen, promote
+ for (OutputInfo *info : _outputMap) {
+ if (info->isProjector == Projector::Maybe) {
+ info->isProjector = Projector::Yes;
+ break;
+ }
+ }
+ }
+ }
+ // Print mappings
+ for (XRRCrtcInfo *info : _crtcMap) {
+ qDebug() << info << ":";
+ for (int i = 0; i < info->npossible; ++i) {
+ qDebug() << "Possible:" << tempMap[info->possible[i]];
+ }
+ }
+ // Determine each screen's position
+ QList<OutputInfo*> screens = _outputMap.values();
+ // Nothing connected?
+ if (screens.isEmpty())
+ return;
+ qSort(screens.begin(), screens.end(), xRectLessThan);
+ int endX = -0xffff;
+ qDebug() << "From left to right";
+ for (OutputInfo* output : screens) {
+ if (output->crtc->x >= endX) {
+ QSize res(0, 0);
+ if (_modeMap.contains(output->crtc->mode)) {
+ auto mode = _modeMap.value(output->crtc->mode);
+ res = QSize(int(mode->width), int(mode->height));
+ }
+ _resolutions.append(res);
+ endX = -0xffff; // Reset
+ }
+ output->position = _resolutions.size() - 1;
+ qDebug() << "Screen (" << output->crtc->x << "," << output->crtc->y << ") @" << output->crtc->width << "x" << output->crtc->height << "as screen" << output->position;
+ if (output->crtc->x + int(output->crtc->width) > endX) {
+ endX = output->crtc->x + int(output->crtc->width);
+ }
+ }
+ qDebug() << "Loaded.";
+}
+QHash<QString, int> ScreenSetup::getScreenPositions() const
+{
+ QHash<QString, int> ret;
+ for (auto oi : _outputMap) {
+ ret.insert(oi->outputName, oi->position);
+ }
+ return ret;
+}
+bool ScreenSetup::readEdid(OutputInfo* output)
+{
+ int numProps = 0;
+ bool found = false;
+ Atom* properties = XRRListOutputProperties(_display, output->id, &numProps);
+ for (int i = 0; i < numProps; ++i) {
+ if (properties[i] == _EDID_ATOM) {
+ found = true;
+ break;
+ }
+ }
+ XFree(properties);
+ if (!found)
+ return false;
+
+ unsigned long nitems, bytes_after;
+ unsigned char *prop;
+ int actual_format;
+ Atom actual_type;
+ bool valid;
+
+ XRRGetOutputProperty(_display, output->id, _EDID_ATOM,
+ 0, 128, False, False,
+ AnyPropertyType,
+ &actual_type, &actual_format,
+ &nitems, &bytes_after, &prop);
+ valid = (actual_format == 8 && nitems >= 128);
+
+ if (valid) {
+ int idx;
+ for (unsigned char *byte = prop + 54; byte < prop + 126; byte += 18) {
+ if (byte[0] != 0 || byte[1] != 0 || byte[2] != 0)
+ continue; // Not a text block
+ if (byte[3] == 0xfc) { // Serial number
+ output->modelName = QString::fromLatin1(reinterpret_cast<const char*>(byte) + 5, 13); // It's actually CP-437 but meh
+ if ((idx = output->modelName.indexOf('\r')) != -1) {
+ output->modelName.truncate(idx);
+ }
+ output->modelName = output->modelName.trimmed();
+ qDebug() << "Display name:" << output->modelName;
+ }
+ }
+ }
+ XFree(prop);
+ return valid;
+}
+/**
+ * Create common modes and add them to all outputs.
+ * Make sure every output has some variant of the most commonly
+ * used modes.
+ */
+void ScreenSetup::initModes()
+{
+ // First copy typical resolutions to all outputs
+#define RES(x,y) (((y) << 16) | (x))
+ QSet<quint32> wanted;
+ wanted << RES(1280, 720) << RES(1280, 800) << RES(1920, 1080);
+ for (XRRModeInfo *mode : _modeMap) {
+ if (toVertRefresh(mode) < 58 || toVertRefresh(mode) > 61)
+ continue; // Play it safe and consider only those for copying that are 60Hz
+ wanted.remove(RES(mode->width, mode->height));
+ // Make sure all outputs got it
+ for (OutputInfo *info : _outputMap) {
+ if (getOutputModeForResolution(info->output, mode->width, mode->height))
+ continue;
+ XRRAddOutputMode(_display, info->id, mode->id);
+ }
+ }
+#undef RES
+ // Create those that no output supported
+ for (auto res : wanted) {
+ unsigned int x = res & 0xffff;
+ unsigned int y = res >> 16;
+ createMode(x, y, 60, QString::asprintf("%ux%u", x, y));
+ }
+ if (!wanted.isEmpty()) {
+ updateScreenResources();
+ }
+ // Finally copy all those the projector supports to other outputs
+ for (auto key : _outputMap.keys()) {
+ OutputInfo *oi = _outputMap[key];
+ if (oi->isProjector == Projector::Yes) {
+ copyModesToAll(key, oi->output->nmode);
+ }
+ }
+ updateScreenResources();
+}
+XRRModeInfo* ScreenSetup::getPreferredMode(OutputInfo *oi) const
+{
+ if (oi->output->nmode == 0) {
+ qDebug() << "getPreferredMode: Output" << oi->outputName << "has no modes!?";
+ return nullptr; // WTF!?
+ }
+ RRMode mode;
+ if (oi->output->npreferred > 0) {
+ mode = oi->output->modes[0];
+ } else {
+ mode = getOutputModeForResolution(oi->output, 1920, 1080);
+ if (mode == None) {
+ mode = getOutputModeForResolution(oi->output, 1280, 720);
+ }
+ if (mode == None) {
+ mode = getOutputModeForResolution(oi->output, 1280, 800);
+ }
+ if (mode == None) {
+ mode = oi->output->modes[0];
+ }
+ }
+ if (!_modeMap.contains(mode))
+ return nullptr;
+ return _modeMap[mode];
+}
+QList<QSize> ScreenSetup::getTotalSize(const QList<OutputInfo*> &projectors, const QList<OutputInfo*> &screens) const
+{
+ const int max = qMax(screens.size(), projectors.size());
+ QList<QSize> modes;
+ for (int i = 0; i < max; ++i) {
+ XRRModeInfo *mode = nullptr;
+ if (i < projectors.size()) {
+ mode = getPreferredMode(projectors.at(i));
+ }
+ if (mode == nullptr && i < screens.size()) {
+ mode = getPreferredMode(screens.at(i));
+ }
+ if (mode != nullptr) {
+ modes.append(QSize(int(mode->width), int(mode->height)));
+ }
+ }
+ return modes;
+}
+static QSize getTotalSizeHorz(const QList<QSize> &list)
+{
+ QSize ret(0, 0);
+ for (auto e : list) {
+ ret.rwidth() += e.width();
+ ret.rheight() = qMax(ret.height(), e.height());
+ }
+ return ret;
+}
+void ScreenSetup::setOutputResolution(OutputInfo *oi, int x, int y, const QSize &size, bool dryRun)
+{
+ RRMode mode = getOutputModeForResolution(oi->output,
+ static_cast<unsigned int>(size.width()), static_cast<unsigned int>(size.height()));
+ if (mode == None) {
+ if (oi->output->nmode == 0)
+ return;
+ qDebug() << oi->outputName << "doesn't support" << size << "- falling back to its default";
+ mode = oi->output->modes[0];
+ }
+ if (!dryRun) {
+ XRRSetCrtcConfig(_display,
+ _screenResources,
+ oi->output->crtc,
+ CurrentTime,
+ x, y, mode,
+ RR_Rotate_0,
+ &oi->id, 1);
+ }
+ qDebug() << "Set" << oi->outputName << "to" << _modeMap[mode]->width << "x" << _modeMap[mode]->height << "-- offset" << x << "/" << y;
+}
-//// qDebug() << XRRSetCrtcConfig(display,
-//// screenResources,
-//// screenResources->crtcs[1],
-//// CurrentTime,
-//// 0, 0,
-//// 597,
-//// RR_Rotate_0,
-//// &(screenResources->outputs[3]),
-//// 1);
-//// qDebug() << XRRSetCrtcConfig(display,
-//// screenResources,
-//// screenResources->crtcs[0],
-//// CurrentTime,
-//// 1920, 0,
-//// 586,
-//// RR_Rotate_0,
-//// &(screenResources->outputs[1]),
-//// 1);
-//// HOLY!!
-//// XRRSetCrtcConfig(display,
-//// screenResources,
-//// screenResources->crtcs[1],
-//// CurrentTime,
-//// 0, 0,
-//// 587,
-//// RR_Rotate_0,
-//// &screenResources->outputs[1],
-//// 1);
+ScreenMode ScreenSetup::getCurrentMode()
+{
+ if (_outputMap.size() == 1)
+ return ScreenMode::Single;
+ if (_outputMap.size() > 2)
+ return ScreenMode::Advanced;
+ for (auto oi : _outputMap) {
+ if (oi->crtc->x != 0 || oi->crtc->y != 0)
+ return ScreenMode::Dual;
+ }
+ return ScreenMode::Clone;
+}
+ScreenMode ScreenSetup::setDefaultMode(bool dryRun)
+{
+ if (_outputMap.size() == 1) // Only one output exists, do nothing
+ return ScreenMode::Single;
+ QMap<QString, OutputInfo*> screenMap;
+ QMap<QString, OutputInfo*> projectorMap;
+ for (auto o : _outputMap) {
+ qDebug() << o->outputName << quint32(o->isProjector);
+ if (o->isProjector == Projector::Yes) {
+ projectorMap.insert(o->outputName, o);
+ } else {
+ screenMap.insert(o->outputName, o);
+ }
+ }
+ auto projectors = projectorMap.values();
+ auto screens = screenMap.values();
+ qDebug() << projectors.size() << "projectors," << screens.size() << "screens.";
+ QList<QSize> outputSizes = getTotalSize(projectors, screens);
+ if (outputSizes.isEmpty())
+ return ScreenMode::Advanced; // Dunno lol
+ QSize screenSize = getTotalSizeHorz(outputSizes);
+ if (!dryRun) {
+ // Disconnect everything
+ for (auto crtc : _crtcMap.keys()) {
+ XRRSetCrtcConfig(_display, _screenResources, crtc, CurrentTime,
+ 0, 0, None, RR_Rotate_0, nullptr, 0);
+ }
+ // Set new screen size
+ XRRSetScreenSize(_display, DefaultRootWindow(_display),
+ screenSize.width(), screenSize.height(),
+ int(25.4 * screenSize.width() / 96.0), // standard dpi that X uses
+ int(25.4 * screenSize.height() / 96.0)); // standard dpi that X uses
+ }
+
+ qDebug() << "Virtual size:" << screenSize << "with" << outputSizes.size() << "different screens.";
+
+ int offset = 0;
+ for (int i = 0; i < outputSizes.size(); ++i) {
+ const QSize &size = outputSizes.at(i);
+ if (i < projectors.size()) {
+ setOutputResolution(projectors.at(i), offset, 0, size, dryRun);
+ }
+ if (i < screens.size()) {
+ setOutputResolution(screens.at(i), offset, 0, size, dryRun);
+ }
+ offset += size.width();
+ }
+ XSync(_display, False);
+ updateScreenResources(); // Re-Read
+ if (outputSizes.size() == 1) // One output size, at least 2 outputs in total -- clone mode
+ return ScreenMode::Clone;
+ if (outputSizes.size() == 2 && _outputMap.size() == 2) // Two outputs, two sizes -- extended
+ return ScreenMode::Dual;
+ return ScreenMode::Advanced; // Must be more than 2 outputs -> something more involved
+}
+/**
+ * Copy first "num" modes from output to all other outputs if they
+ * dont have a suitable mode yet.
+ */
+void ScreenSetup::copyModesToAll(RROutput sourceId, int num)
+{
+ const XRROutputInfo *outputInfo = _outputMap[sourceId]->output;
+ for (int i = 0; i < num; ++i) {
+ if (!_modeMap.contains(outputInfo->modes[i])) {
+ qDebug() << "BUG: Mode" << outputInfo->modes[i] << "not found in copyModesToAll";
+ continue;
+ }
+ const XRRModeInfo *mode = _modeMap[outputInfo->modes[i]];
+ for (auto other : _outputMap.keys()) {
+ if (other == sourceId)
+ continue;
+ const XRROutputInfo *otherInfo = _outputMap[other]->output;
+ if (getOutputModeForResolution(otherInfo, mode->width, mode->height) != None)
+ continue;
+ XRRAddOutputMode(_display, other, outputInfo->modes[i]);
+ }
+ }
+}
+/**
+ * Get the RRMode for the specified output matching the given resolution.
+ * If multiple modes match the given resolution, the function will
+ * return the first matching preferred mode. If no preferred
+ * mode matches, the non-preferred mode with the highest refresh rate
+ * will be returned.
+ * Otherwise, None will be returned.
+ */
+RRMode ScreenSetup::getOutputModeForResolution(const XRROutputInfo *output, unsigned int width, unsigned int height) const
+{
+ XRRModeInfo *retval = nullptr;
+ for (int i = 0; i < output->nmode; ++i) {
+ if (!_modeMap.contains(output->modes[i]))
+ continue;
+ XRRModeInfo *info = _modeMap[output->modes[i]];
+ if (info->width == width && info->height == height) {
+ if (i < output->npreferred)
+ return output->modes[i];
+ if (retval == nullptr || retval->dotClock < info->dotClock) {
+ retval = info;
+ }
+ }
+ }
+ return retval == nullptr ? None : retval->id;
+}
+//___________________________________________________________________________
+bool ScreenSetup::createMode(unsigned int resX, unsigned int resY, float refresh, QString name)
+{
+ QByteArray ba = name.toLocal8Bit();
+ mode *mode = vert_refresh(int(resX), int(resY), refresh, 0, 0, 0);
+ if (mode == nullptr)
+ return false;
+ XRRModeInfo m;
+ m.width = static_cast<unsigned int>(mode->hr);
+ m.height = static_cast<unsigned int>(mode->vr);
+ m.dotClock = static_cast<unsigned long>(mode->pclk) * 1000ul * 1000ul;
+ m.hSyncStart= static_cast<unsigned int>(mode->hss);
+ m.hSyncEnd = static_cast<unsigned int>(mode->hse);
+ m.hTotal = static_cast<unsigned int>(mode->hfl);
+ m.hSkew = 0;
+ m.vSyncStart= static_cast<unsigned int>(mode->vss);
+ m.vSyncEnd = static_cast<unsigned int>(mode->vse);
+ m.vTotal = static_cast<unsigned int>(mode->vfl);
+ m.id = 0;
+ m.name = ba.data();
+ m.nameLength = static_cast<unsigned int>(ba.length());
+ free(mode);
+
+ for (XRRModeInfo *mode : _modeMap) {
+ if (mode->width == m.width && mode->height == m.height && mode->dotClock == m.dotClock)
+ return true; // Already exists, return true?
+ }
+
+ RRMode xid = XRRCreateMode(_display, DefaultRootWindow(_display), &m);
+ qDebug() << "Return value of create was" << xid;
+ // Immediately add to all screens
+ for (OutputInfo *info : _outputMap) {
+ XRRAddOutputMode(_display, info->id, xid);
+ }
+ return true;
+}
+//___________________________________________________________________________
+ScreenSetup::~ScreenSetup()
+{
+ freeResources();
+ XCloseDisplay(_display);
+}
+bool ScreenSetup::applyChanges()
+{
+ return true;
+}
+void ScreenSetup::revertChanges()
+{
+}
-/////////////////////////////// DBEUG //// DEBUG //////////////////////////////////
-//// //SCREEN
-//// qDebug() << "ScreenResources: ";
-//// qDebug() << "Count of crtcs: " << screenResources->ncrtc;
-//// qDebug() << "Count of outputs: " << screenResources->noutput;
-//// qDebug() << "Count of modes: " << screenResources->nmode;
+static bool modeBiggerThan(const QPair<quint32, quint32> &a, const QPair<quint32, quint32> &b)
+{
+ if (a.first > b.first)
+ return true;
+ if (a.first == b.first)
+ return a.second > b.second;
+ return false;
+}
-//// //MODES
-//// for (int i = 0; i < screenResources->nmode; ++i)
-//// {
-//// qDebug() << screenResources->modes[i].id
-//// << screenResources->modes[i].width
-//// << screenResources->modes[i].height
-////// << screenResources->modes[i].dotClock
-////// << screenResources->modes[i].hSyncStart
-////// << screenResources->modes[i].hSyncEnd
-////// << screenResources->modes[i].hTotal
-////// << screenResources->modes[i].hSkew
-////// << screenResources->modes[i].vSyncStart
-////// << screenResources->modes[i].vSyncEnd
-////// << screenResources->modes[i].vTotal
-////// << screenResources->modes[i].nameLength
-//// << screenResources->modes[i].name;
-//// }
+QList<QPair<quint32, quint32>> ScreenSetup::getCommonModes() const
+{
+ QHash<QPair<quint32, quint32>, QSet<RROutput>> matches;
+ for (auto oi : _outputMap) {
+ for (int i = 0; i < oi->output->nmode; ++i) {
+ if (!_modeMap.contains(oi->output->modes[i]))
+ continue;
+ const auto mode = _modeMap[oi->output->modes[i]];
+ const QPair<quint32, quint32> pair = qMakePair(mode->width, mode->height);
+ matches[pair].insert(oi->id);
+ }
+ }
+ QList<QPair<quint32, quint32>> ret;
+ for (auto it = matches.begin(); it != matches.end(); ++it) {
+ if (it.value().size() == _outputMap.size()) {
+ ret.append(it.key());
+ }
+ }
+ qSort(ret.begin(), ret.end(), modeBiggerThan);
+ return ret;
+}
-//// //CRTCS
-//// for (int j = 0; j < screenResources->ncrtc; ++j)
-//// {
-//// XRRCrtcInfo *CrtcInfo = XRRGetCrtcInfo(display, screenResources, screenResources->crtcs[j]);
-//// qDebug() << "\n-------- CrtcInfo";
-//// qDebug() << "timestamp: " << CrtcInfo->timestamp;
-//// qDebug() << "x: " << CrtcInfo->x;
-//// qDebug() << "y: " << CrtcInfo->y;
-//// qDebug() << "width: " << CrtcInfo->width;
-//// qDebug() << "height: " << CrtcInfo->height;
-//// qDebug() << "rotation: " << CrtcInfo->rotation;
-//// qDebug() << "noutput: " << CrtcInfo->noutput;
-//// qDebug() << "rotations: " << CrtcInfo->rotations;
-//// qDebug() << "npossible: " << CrtcInfo->npossible;
-//// XRRFreeCrtcInfo(CrtcInfo);
-//// }
-//// // OUTPUTS
-//// for (int nOut = 0; nOut < screenResources->noutput; ++nOut)
-//// {
-//// XRROutputInfo *OutputInfo = XRRGetOutputInfo (display, screenResources, screenResources->outputs[nOut]);
-////// if (OutputInfo->connection == RR_Connected) {
-//// qDebug() << "\n--- Output " << nOut;
-//// qDebug() << "name " << OutputInfo->name;
-//// qDebug() << "mm_width " << OutputInfo->mm_width;
-//// qDebug() << "mm_height " << OutputInfo->mm_height;
-//// qDebug() << "ncrtc " << OutputInfo->ncrtc;
-//// qDebug() << "nclone " << OutputInfo->nclone;
-//// qDebug() << "nmode " << OutputInfo->nmode;
-//// qDebug() << "npreferred " << OutputInfo->npreferred;
-
-//// for (int j = 0; j < OutputInfo->nmode; j++)
-//// {
-//// qDebug() << "mode" << j << ": " << OutputInfo->modes[j];
-//// }
-////// }
-//// XRRFreeOutputInfo (OutputInfo);
-//// }
-///////////////////////////////////////////////////////////////////////////////////
diff --git a/src/x.h b/src/x.h
index 4ca54a1..50008c4 100644
--- a/src/x.h
+++ b/src/x.h
@@ -3,7 +3,7 @@
#include <QDebug>
#include <QList>
-#include <QMap>
+#include <QHash>
#include <QString>
#include <QRect>
#include <QSet>
@@ -11,285 +11,68 @@
#include <X11/Xlib.h>
#include <X11/extensions/Xrandr.h>
-namespace X
-{
- class Mode;
- class Output;
- class Crtc;
-
-
- typedef QSet<RRMode> ModeSet;
- typedef QSet<RRCrtc> CrtcSet;
- typedef QSet<RROutput> OutputSet;
-
- typedef QList<RRMode> ModeList;
- typedef QList<RRCrtc> CrtcList;
- typedef QList<RROutput> OutputList;
-
-
-
- ///////////////////////////////////////////////////////////////////////////
-
-
- class Screen
- {
- friend class Crtc;
- friend class Output;
-
- public:
-
- typedef QMap<RRMode,Mode> ModeMap;
- typedef QMap<RRCrtc,Crtc> CrtcMap;
- typedef QMap<RROutput,Output*> OutputMap;
-
- int applyChanges();
- void createMode(
- unsigned int resX,
- unsigned int resY,
- unsigned long dotClock,
- unsigned int hSyncStart,
- unsigned int hSyncEnd,
- unsigned int hTotal,
- unsigned int vSyncStart,
- unsigned int vSyncEnd,
- unsigned int vTotal,
- QString name);
- void revertChanges();
- void updateScreenResources();
-
- // Getters
- inline const Display* display() const {return _display;}
- inline const XRRScreenResources* screenResources() const {return _screenResources;}
- inline const ModeMap& getModeMap() const {return _modeMap;}
- inline const OutputMap& getOutputMap() const {return _outputMap;}
- inline const OutputList& getConnectedOutputList() const {return _connectedOutputList;}
-
- // Singleton
- inline static Screen* inst() {
- if (_instance == 0) _instance = new Screen();
- return _instance;
- }
-
- private:
- Screen();
- ~Screen();
-
- static Screen * _instance;
- Display* _display;
- XRRScreenResources* _screenResources;
- ModeMap _modeMap;
- CrtcMap _crtcMap;
- OutputMap _outputMap;
-
- OutputList _connectedOutputList;
- };
-
- ///////////////////////////////////////////////////////////////////////////
-
-
- class XElement
- {
- public:
- XElement(XID = 0);
- XID _id;
- bool _validity;
- inline XID getID() const {return _id;}
- inline XID isValid() const {return _validity;}
- };
-
-
- ///////////////////////////////////////////////////////////////////////////
-
-
- class Mode : public XElement
- {
- public:
- Mode(XRRModeInfo* = NULL);
-
- // Xlib internal stuff
- QSize _resolution;
- unsigned long _dotClock;
- unsigned int _hSyncStart;
- unsigned int _hSyncEnd;
- unsigned int _hTotal;
- unsigned int _hSkew;
- unsigned int _vSyncStart;
- unsigned int _vSyncEnd;
- unsigned int _vTotal;
- QString _name;
- XRRModeFlags _modeFlags;
- };
-
-
- ///////////////////////////////////////////////////////////////////////////
-
-
- class Output : public XElement
- {
- friend int Screen::applyChanges();
- friend void Screen::revertChanges();
-
- typedef enum _State {
- Connected = RR_Connected,
- Disconnected = RR_Disconnected,
- Unknown = RR_UnknownConnection
- } State;
-
- public:
-
- Output(XID, Screen*);
-
- /** Public interface to modify output settings.
- * This function is the only interface to the outside, which is able to
- * change something in this object.
- * @param active Indicates wheter the output shall be on or off.
- * @param mode The mode wich is used for the output.
- * @param position The position of the topleft corner on the screen.
- * @return 0 if the config passed teh sanity checks.
- */
- int changeConfiguration(bool active, XID mode, QPoint position);
+struct OutputInfo;
- inline QString getName() const {return _name;}
- inline ModeSet getModeSet() const {return _modes;}
- inline RRMode getPreferred() const {return _preferred;}
- inline bool isActive() const {return !_crtcs.isEmpty();}
- inline bool isConnected() const {return _connection == Connected;}
- inline bool isProjector() const {return _isProjector;}
- inline bool hasReliableEDID() const {return _hasReliableEDID;}
+///////////////////////////////////////////////////////////////////////////
- private:
-
- Screen *_parent;
-
- // Indicates when the configuration was last set.
- Time _timestamp;
-
- // The current source CRTC for video data, or Disabled if the
- // output is not connected to any CRTC.
- RRCrtc _crtc;
-
- // UTF-8 encoded string designed to be presented to the
- // user to indicate which output this is. E.g. "S-Video" or "DVI".
- QString _name;
-
- // 'widthInMillimeters' and 'heightInMillimeters' report the physical
- // size of the displayed area. If unknown, or not really fixed (e.g.,
- // for a projector), these values are both zero.
- QSize _metricDimension;
-
- // Indicates whether the hardware was able to detect a
- // device connected to this output. If the hardware cannot determine
- // whether something is connected, it will set this to
- // UnknownConnection.
- State _connection;
-
-// // Contains the resulting subpixel order of the
-// // connected device to allow correct subpixel rendering.
-// SubpixelOrder _subpixel_order;
-
- // The list of CRTCs that this output may be connected to.
- // Attempting to connect this output to a different CRTC results in a
- // Match error.
- CrtcList _crtcs;
-
- // The list of outputs which may be simultaneously
- // connected to the same CRTC along with this output. Attempting to
- // connect this output with an output not in this list
- // results in a Match error.
- OutputList _clones;
-
- // The list of modes supported by this output. Attempting to
- // connect this output to a CRTC not using one of these modes results
- // in a Match error.
- ModeSet _modes;
-
- // The first 'num-preferred' modes in 'modes' are preferred by the
- // monitor in some way; for fixed-pixel devices, this would generally
- // indicate which modes match the resolution of the output device.
- RRMode _preferred;
-
- // Indicates wheter this is a beamer or not.
- bool _isProjector;
-
- // Indicates wheter the output received reliable data over the DDC.
- // The Display Data Channel, or DDC, is a collection of protocols for
- // digital communication between a computer display and a graphics
- // adapter that enable the display to communicate its supported display
- // modes to the adapter and that enable the computer host to adjust
- // monitor parameters, such as brightness and contrast.
- // Extended display identification data (EDID) is a data structure
- // provided by a digital display to describe its capabilities to a
- // video source.
- bool _hasReliableEDID;
- };
-
-
- ///////////////////////////////////////////////////////////////////////////
-
-
- class Crtc : public XElement
- {
- friend int Screen::applyChanges();
- friend void Screen::revertChanges();
- friend int Output::changeConfiguration(bool active, XID mode, QPoint position);
- //friend int Output::changeConfiguration(bool active, XID mode, QPoint position);
-
- public:
-
- Crtc(XID, Screen*);
-
- // Getter
- inline const OutputList & getConnectedOutputs( ) { return _outputs; }
- inline const QRect getRect() { return _crtcRect; }
-
- // Setter
-
-
- // Applies the changes made to this crtc
-// int applyChanges();
-// void disable();
-// void connect(const Output *, Mode);
-
- private:
-
- Screen *_parent;
-
- // Indicates when the configuration was last set.
- Time _timestamp;
-
- // 'x' and 'y' indicate the position of this CRTC within the screen
- // region. They will be set to 0 when the CRTC is disabled.
- // 'width' and 'height' indicate the size of the area within the screen
- // presented by this CRTC. This may be different than the size of the
- // mode due to rotation, the projective transform, and the Border
- // property described below.
- // They will be set to 0 when the CRTC is disabled.
- QRect _crtcRect;
-
- // Indicates which mode is active, or None indicating that the
- // CRTC has been disabled and is not displaying the screen contents.
- RRMode _mode;
-
- // The list of outputs currently connected to this CRTC,
- // is empty when the CRTC is disabled.
- OutputList _outputs;
-
- // Lists all of the outputs which may be connected to this CRTC.
- OutputList _possible;
-
- // The active rotation. Set to Rotate_0 when the CRTC is disabled.
- Rotation _rotation;
- // enum Rotation {
- // Normal = RR_Rotate_0,
- // Left = RR_Rotate_270,
- // Right = RR_Rotate_90,
- // UpsideDown = RR_Rotate_180
- // };
+enum class ScreenMode
+{
+ Single,
+ Clone,
+ Dual,
+ Advanced,
+};
- // contains the set of rotations and reflections supported by the CRTC
- Rotation _rotations;
- };
-}
+class ScreenSetup
+{
+public:
+
+ typedef QHash<RRMode, XRRModeInfo*> ModeMap;
+ typedef QHash<RRCrtc, XRRCrtcInfo*> CrtcMap;
+ typedef QHash<RROutput, OutputInfo*> OutputMap;
+ typedef QVector<QSize> ResolutionVector;
+
+ void updateScreenResources();
+ bool applyChanges();
+ void initModes();
+ XRRModeInfo* getPreferredMode(OutputInfo *oi) const;
+ QList<QSize> getTotalSize(const QList<OutputInfo*> &projectors, const QList<OutputInfo*> &screens) const;
+ void setOutputResolution(OutputInfo *oi, int x, int y, const QSize &size, bool dryRun = false);
+ ScreenMode getCurrentMode();
+ ScreenMode setDefaultMode(bool dryRun = false);
+ void copyModesToAll(RROutput id, int num);
+ RRMode getOutputModeForResolution(const XRROutputInfo *output, unsigned int width, unsigned int height) const;
+ bool createMode(unsigned int resX, unsigned int resY, float refresh, QString name);
+ void revertChanges();
+ QList<QPair<quint32, quint32>> getCommonModes() const;
+ int getOutputCount() const { return _outputMap.size(); }
+ QHash<QString, int> getScreenPositions() const;
+ const ResolutionVector &getVirtualResolutions() const { return _resolutions; }
+
+ // Singleton
+ inline static ScreenSetup* inst() {
+ if (_instance == nullptr) _instance = new ScreenSetup();
+ return _instance;
+ }
+
+private:
+ ScreenSetup();
+ ~ScreenSetup();
+
+ void freeResources();
+ bool readEdid(OutputInfo* output);
+
+ static ScreenSetup * _instance;
+ Display* _display;
+ Atom _EDID_ATOM;
+ XRRScreenResources* _screenResources;
+ ModeMap _modeMap;
+ CrtcMap _crtcMap;
+ OutputMap _outputMap;
+ ResolutionVector _resolutions;
+};
+
+///////////////////////////////////////////////////////////////////////////
#endif // XRANDR_H