diff options
author | Simon Rettberg | 2018-08-31 10:57:39 +0200 |
---|---|---|
committer | Simon Rettberg | 2018-08-31 10:57:39 +0200 |
commit | 64af75c336424b6dfe0a114a4697fb522a6169e5 (patch) | |
tree | 3d6794b2ad0b55e33df109035f41a8291eddf864 | |
parent | Make wakeup work (diff) | |
download | beamergui-64af75c336424b6dfe0a114a4697fb522a6169e5.tar.gz beamergui-64af75c336424b6dfe0a114a4697fb522a6169e5.tar.xz beamergui-64af75c336424b6dfe0a114a4697fb522a6169e5.zip |
Update
-rw-r--r-- | LICENSE | 2 | ||||
-rw-r--r-- | src/i18n/de.ts | 26 | ||||
-rw-r--r-- | src/icons.qrc | 5 | ||||
-rw-r--r-- | src/icons/projector_icon.svg | 114 | ||||
-rw-r--r-- | src/icons/refresh_icon.svg | 37 | ||||
-rw-r--r-- | src/icons/screen_icon.svg | 39 | ||||
-rw-r--r-- | src/main.cpp | 29 | ||||
-rw-r--r-- | src/widget.cpp | 182 | ||||
-rw-r--r-- | src/widget.h | 3 | ||||
-rw-r--r-- | src/widget.ui | 23 | ||||
-rw-r--r-- | src/xprivate.cpp | 38 | ||||
-rw-r--r-- | src/xx.cpp | 77 | ||||
-rw-r--r-- | src/xx.h | 16 | ||||
-rw-r--r-- | udev/99-beamergui.rules | 3 |
14 files changed, 386 insertions, 208 deletions
@@ -1,7 +1,7 @@ GPL blabla Icons: -Projector: +Projector + Refresh: Icons made by "http://www.freepik.com" Freepik, from "https://www.flaticon.com/", "Creative Commons BY 3.0" (CC 3.0 BY) Screen: diff --git a/src/i18n/de.ts b/src/i18n/de.ts index b8537b6..5589628 100644 --- a/src/i18n/de.ts +++ b/src/i18n/de.ts @@ -4,7 +4,7 @@ <context> <name>QCoreApplication</name> <message> - <location filename="../widget.cpp" line="191"/> + <location filename="../widget.cpp" line="216"/> <source>%1x%2</source> <translation type="unfinished"></translation> </message> @@ -67,37 +67,37 @@ <translation type="unfinished"></translation> </message> <message> - <location filename="../widget.cpp" line="327"/> + <location filename="../widget.cpp" line="364"/> <source>(off)</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../widget.cpp" line="359"/> + <location filename="../widget.cpp" line="396"/> <source>Output</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../widget.cpp" line="360"/> + <location filename="../widget.cpp" line="397"/> <source>Position</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../widget.cpp" line="471"/> + <location filename="../widget.cpp" line="508"/> <source>Do you want to keep this resolution?</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../widget.cpp" line="472"/> + <location filename="../widget.cpp" line="509"/> <source>Keep</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../widget.cpp" line="568"/> + <location filename="../widget.cpp" line="605"/> <source>Confirm</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../widget.cpp" line="569"/> + <location filename="../widget.cpp" line="606"/> <source>This terminates the GUI. It will not pop up again if further screens are connected. Are you sure?</source> @@ -107,27 +107,27 @@ Are you sure?</source> <context> <name>main</name> <message> - <location filename="../main.cpp" line="75"/> + <location filename="../main.cpp" line="86"/> <source>Automatically configure modes and set up screens.</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../main.cpp" line="79"/> + <location filename="../main.cpp" line="90"/> <source>Show config GUI if more than one screen is connected.</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../main.cpp" line="83"/> + <location filename="../main.cpp" line="94"/> <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="87"/> + <location filename="../main.cpp" line="98"/> <source>Test mode, don't actually apply any changes.</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../main.cpp" line="91"/> + <location filename="../main.cpp" line="102"/> <source>Connect to system bus to trigger wakeup of wainting beamergui.</source> <translation type="unfinished"></translation> </message> diff --git a/src/icons.qrc b/src/icons.qrc index 3a700e5..164f3b1 100644 --- a/src/icons.qrc +++ b/src/icons.qrc @@ -1,7 +1,8 @@ <RCC> - <qresource> + <qresource prefix="/"> <file alias="projector">icons/projector_icon.svg</file> <file alias="screen">icons/screen_icon.svg</file> <file alias="check">icons/checkmark.svg</file> - </qresource> + <file alias="refresh">icons/refresh_icon.svg</file> + </qresource> </RCC> diff --git a/src/icons/projector_icon.svg b/src/icons/projector_icon.svg index a22f105..d145e98 100644 --- a/src/icons/projector_icon.svg +++ b/src/icons/projector_icon.svg @@ -1,65 +1,53 @@ -<?xml version="1.0" encoding="iso-8859-1"?>
-<!-- Generator: Adobe Illustrator 18.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
-<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
- viewBox="0 0 494.516 494.516" style="enable-background:new 0 0 494.516 494.516;" xml:space="preserve">
-<g>
- <path d="M264.787,220.756c1.088,0,2.176-0.415,3.004-1.245l23.453-23.454c1.66-1.66,1.66-4.351,0-6.011
- c-1.658-1.659-4.352-1.659-6.01,0l-23.453,23.454c-1.66,1.66-1.66,4.351,0,6.011C262.611,220.341,263.699,220.756,264.787,220.756z
- "/>
- <path d="M316.324,190.047l-54.543,54.544c-1.66,1.66-1.66,4.351,0,6.011c0.83,0.83,1.918,1.245,3.006,1.245
- c1.088,0,2.176-0.415,3.004-1.245l54.543-54.544c1.66-1.66,1.66-4.351,0-6.011C320.674,188.388,317.982,188.388,316.324,190.047z"
- />
- <path d="M308.49,167.516c-39.314,0-71.301,31.985-71.301,71.299c0,39.315,31.986,71.3,71.301,71.3
- c39.314,0,71.299-31.985,71.299-71.3C379.789,199.501,347.805,167.516,308.49,167.516z M308.49,301.615
- c-34.629,0-62.801-28.172-62.801-62.8c0-34.627,28.172-62.799,62.801-62.799c34.627,0,62.799,28.172,62.799,62.799
- C371.289,273.443,343.117,301.615,308.49,301.615z"/>
- <path d="M421.185,151.672H352.9c-13.334-6.823-28.43-10.676-44.41-10.676c-15.979,0-31.076,3.853-44.41,10.676H73.33
- C32.896,151.672,0,184.568,0,225.003v27.626c0,37.031,27.598,67.72,63.303,72.626v7.565c0,11.413,9.285,20.699,20.699,20.699
- h16.447c11.414,0,20.699-9.286,20.699-20.699v-6.861H264.08c13.334,6.823,28.432,10.676,44.41,10.676h0.002
- c15.979,0,31.074-3.853,44.41-10.676h20.465v6.861c0,11.413,9.285,20.699,20.699,20.699h16.447
- c11.414,0,20.699-9.286,20.699-20.699v-7.565c35.705-4.906,63.303-35.595,63.303-72.626v-27.626
- C494.516,184.568,461.621,151.672,421.185,151.672z M112.648,332.82c0,6.727-5.473,12.199-12.199,12.199H84.002
- c-6.727,0-12.199-5.472-12.199-12.199v-6.9c0.51,0.01,1.016,0.039,1.527,0.039h39.318V332.82z M73.33,317.459
- c-21.209,0-40.072-10.238-51.908-26.032h51.223c2.348,0,4.25-1.903,4.25-4.25c0-2.347-1.902-4.25-4.25-4.25H16.039
- c-4.805-9.049-7.539-19.358-7.539-30.298v-9.564h188.621c2.348,0,4.25-1.903,4.25-4.25s-1.902-4.25-4.25-4.25H8.5v-9.562
- c0-6.332,0.928-12.448,2.629-18.237h101.617c2.348,0,4.25-1.903,4.25-4.25s-1.902-4.25-4.25-4.25H14.293
- c10.205-22.442,32.82-38.094,59.037-38.094h177.057c-24.076,17.836-39.717,46.446-39.717,78.643
- c0,32.197,15.641,60.808,39.717,78.644H73.33z M308.49,328.135c-49.25,0-89.32-40.068-89.32-89.319
- c0-49.251,40.07-89.319,89.32-89.319c49.252,0,89.32,40.068,89.32,89.319c0,49.251-40.068,89.319-89.318,89.319H308.49z
- M422.713,332.82c0,6.727-5.473,12.199-12.199,12.199h-16.447c-6.727,0-12.199-5.472-12.199-12.199v-6.861h39.318
- c0.512,0,1.018-0.028,1.527-0.039V332.82z M421.185,317.459h-54.592c24.078-17.836,39.717-46.446,39.717-78.644
- c0-32.197-15.641-60.807-39.719-78.643h54.594c35.748,0,64.83,29.083,64.83,64.831v11.137h-21.688c-2.348,0-4.25,1.903-4.25,4.25
- s1.902,4.25,4.25,4.25h21.688v7.989C486.016,288.376,456.934,317.459,421.185,317.459z"/>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
+<?xml version="1.0" encoding="utf-8"?>
+<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" width="506" height="226">
+<g transform="translate(5.6843745,0)">
+ <path d="m 264.787,86.533051 c 1.088,0 2.176,-0.415 3.004,-1.245 l 23.453,-23.454 c 1.66,-1.66 1.66,-4.351
+ 0,-6.011 -1.658,-1.659 -4.352,-1.659 -6.01,0 l -23.453,23.454 c -1.66,1.66 -1.66,4.351 0,6.011 0.83,0.83
+ 1.918,1.245 3.006,1.245 z"/>
+ <path d="m 316.324,55.824051 -54.543,54.543999 c -1.66,1.66 -1.66,4.351 0,6.011 0.83,0.83 1.918,1.245
+ 3.006,1.245 1.088,0 2.176,-0.415 3.004,-1.245 l 54.543,-54.543999 c 1.66,-1.66 1.66,-4.351 0,-6.011
+ -1.66,-1.659 -4.352,-1.659 -6.01,0 z"/>
+ <path d="m 306.62695,31.316111 c -33.18717,0.25249 -63.80337,25.96705 -69.95117,58.54883 -6.90988,31.106029
+ 9.40608,65.280469 37.80613,79.642789 28.28598,15.46633 66.25331,8.906 87.74203,-15.11728
+ 22.53369,-23.43249 26.00409,-62.240189 7.987,-89.298949 -13.48926,-21.33267 -38.32032,-34.63681
+ -63.58399,-33.77539 z m 1.86328,12.47656 c 29.01072,-0.63446 56.23968,22.6771 60.10157,51.57618
+ 5.00138,28.643539 -13.80349,58.797369 -41.59946,67.141169 -26.27167,8.9673 -57.71075,-3.16886
+ -71.22672,-27.66266 -14.84977,-24.69204 -8.54358,-59.307669 14.08985,-77.166009 10.82519,-8.97579
+ 24.65521,-13.93047 38.63476,-13.88868 z"/>
+ <path d="m 306.95898,2.7848606 c -15.16014,0.20678 -30.26935,3.86572 -43.82421,10.6640704 -66.33811,0.32111
+ -132.71013,-0.65448 -199.027348,0.50195 -36.88034,3.81003 -68.1757706,37.6184 -67.9770919,74.92075
+ -0.11974,15.728089 -1.28154,31.763209 1.8410828,47.270329 6.7502267,29.04757 31.6537591,53.16732
+ 61.1340561,58.28353 -1.435617,13.25989 8.021284,27.64848 21.970703,28.58594 8.923315,0.71852
+ 18.134142,0.91421 26.863278,-0.81836 10.98672,-3.32996 18.17661,-15.07973 17.4336,-26.23829
+ 18.89059,-0.47804 37.90443,-0.0675 56.8457,-0.21875 26.9694,0 53.9388,0 80.9082,0 27.97776,14.19909
+ 62.65545,14.19634 90.64844,0.0488 4.99941,0.68075 11.22902,-1.39507 15.41211,0.61328 -1.17524,14.51864
+ 12.11467,28.65073 26.91406,26.78125 11.25833,0.67766 24.8809,1.42157 32.9043,-8.14843 5.01958,-5.52579
+ 6.70326,-13.30572 6.33398,-20.5918 36.54533,-6.13315 65.47804,-41.8142 63.06646,-78.96801 0.67317,-21.787919
+ 1.56059,-45.412209 -10.89409,-64.472679 -14.51203,-24.19526 -42.66168,-39.44002 -70.93204,-37.57898
+ -21.46793,-0.43562 -43.31328,1.1073 -64.52939,-0.85142 -14.0115,-6.6728004 -29.57625,-10.0359804
+ -45.0918,-9.7832104 z m 3.28711,16.4785204 c 39.4604,0.0658 75.89146,31.41223 82.27149,70.02344
+ 6.79984,34.983409 -11.44403,73.520729 -43.13477,90.267569 -32.78726,18.23663 -76.56419,11.75134
+ -102.39453,-15.76172 -24.88947,-25.53578 -31.3338,-67.053579 -14.18555,-98.699209 14.6625,-28.09909
+ 45.60502,-46.90957 77.44336,-45.83008 z m 112.33399,10.67383 c 32.13876,-0.0197 60.69034,29.32254
+ 59.44336,61.44922 -0.69558,2.25642 1.8751,7.80675 -2.23242,6.70117 -7.08467,0.35634 -15.90425,-2.42077
+ -21.81446,2.685539 -5.01715,5.68783 0.85519,15.60252 8.3711,13.64453 5.1198,0.31176 11.40865,-0.61399
+ 15.82226,0.44532 1.63171,29.03662 -19.55265,57.82899 -48.56054,63.03125 -18.48076,3.19513 -37.49076,0.17533
+ -56.08985,1.51367 31.99079,-28.68807 42.17176,-78.68532 23.4213,-117.454169 -5.58165,-12.04358
+ -13.53357,-22.98632 -23.27481,-32.00481 14.97133,-0.009 29.94281,0.0167 44.91406,-0.0117 z m -350.277346,0.01
+ c 55.645176,0.005 111.290366,6.4e-4 166.935546,0.002 -31.38309,28.43673 -41.74351,77.417349
+ -23.81363,115.925109 5.60217,12.57373 13.77638,23.99845 23.87613,33.35223 -58.15499,-0.33709
+ -116.36174,0.74961 -174.484374,-0.57422 -13.072871,-1.8228 -25.454131,-8.10601 -34.722656,-17.49219
+ 15.266585,-0.30705 30.7027,0.87778 45.873047,-0.61523 7.727176,-2.72104 5.50485,-15.99864 -2.785156,-15.81641
+ -18.232389,-0.0455 -36.464843,-0.018 -54.697266,-0.0234 -4.916353,-9.88878 -6.381031,-20.98578
+ -6.056641,-31.86523 62.408265,-0.23294 124.883136,0.48231 187.249996,-0.36914 8.05374,-1.95579
+ 6.99391,-15.613319 -1.40039,-16.021479 -61.9353,-0.24209 -123.871107,-0.0443 -185.806637,-0.10547
+ -0.09346,-6.63177 0.09723,-13.34266 1.712891,-19.8086 33.89003,-0.3703 67.912224,0.78478 101.722656,-0.58203
+ 7.88019,-2.58382 5.74789,-16.0619 -2.61914,-15.88672 -30.782327,-0.17583 -61.56644,0.22381 -92.347657,-0.17773
+ 10.587263,-18.07301 30.707654,-29.79202 51.363281,-29.94141 z M 386.96094,195.31611 c 10.51484,1.13977
+ 21.75903,-0.14334 32.07422,0.61524 0.87866,6.65214 -5.30372,12.67167 -11.91993,10.71484 -6.64769,-0.33544
+ -16.41047,2.75342 -20.41015,-4.45117 -0.17534,-1.17232 -2.59486,-6.95399 0.25586,-6.87891 z
+ m -310.761721,0.11524 c 10.789569,0.85487 22.190459,-0.15849 32.767581,0.48632 0.89634,6.67101
+ -5.30259,12.66113 -11.912112,10.72657 -6.714172,-0.31317 -17.086942,2.86291 -20.597657,-4.99414
+ -0.207806,-1.53014 -1.947751,-5.29317 -0.257812,-6.21875 z"/>
</g>
</svg>
diff --git a/src/icons/refresh_icon.svg b/src/icons/refresh_icon.svg new file mode 100644 index 0000000..1660754 --- /dev/null +++ b/src/icons/refresh_icon.svg @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="iso-8859-1"?> +<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Capa_1" x="0px" y="0px" viewBox="0 0 279.881 279.881" style="enable-background:new 0 0 279.881 279.881;" xml:space="preserve" width="512px" height="512px"> +<g> + <path d="M153.905,130.755c2.562-5.39,5.695-12.222,8.169-18.4c2.241-5.57-0.315-13.201-5.314-16.529 C124.589,74.369,15.465,12.559,3.118,151.467c-0.533,5.983-1.594,6.054-2.187,0.076C-1.157,130.63-2.13,84.415,25.457,59.531 c4.46-4.019,12.319-9.801,17.345-13.086c17.884-11.71,62.767-31.857,127.318,2.594c5.303,2.828,10.476,0.598,11.727-5.276 l4.493-21.142c1.246-5.874,4.775-6.467,7.887-1.327l52.917,87.563c3.106,5.14,0.865,10.334-5.009,11.607l-82.375,17.764 C153.878,139.496,151.327,136.178,153.905,130.755z M236.247,239.357c-17.884,11.694-62.761,31.802-127.318-2.622 c-5.303-2.828-11.047-0.756-12.994,4.928l-5.466,15.958c-1.947,5.684-5.793,5.978-8.588,0.658l-42.256-80.444 c-2.796-5.319-0.402-11.052,5.347-12.804l69.832-21.332c5.744-1.751,9.1,1.517,7.5,7.316c-1.795,6.489-4.117,14.947-6.081,22.175 c-1.572,5.798,1.137,13.353,6.206,16.589c32.553,20.766,142.743,80.319,154.741-56.653c0.522-5.983,1.496-6.032,2.007-0.049 c1.806,21.153,2.197,68.157-25.58,93.198C249.133,230.29,241.279,236.072,236.247,239.357z" fill="#00942c"/> +</g> +<g> +</g> +<g> +</g> +<g> +</g> +<g> +</g> +<g> +</g> +<g> +</g> +<g> +</g> +<g> +</g> +<g> +</g> +<g> +</g> +<g> +</g> +<g> +</g> +<g> +</g> +<g> +</g> +<g> +</g> +</svg> diff --git a/src/icons/screen_icon.svg b/src/icons/screen_icon.svg index f2ca511..d4f0ba4 100644 --- a/src/icons/screen_icon.svg +++ b/src/icons/screen_icon.svg @@ -1,44 +1,9 @@ -<?xml version="1.0" encoding="iso-8859-1"?>
-<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
-<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
- width="612px" height="612px" viewBox="0 0 612 612" style="enable-background:new 0 0 612 612;" xml:space="preserve">
-<g>
+<?xml version="1.0" encoding="utf-8"?>
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="612" height="612">
<path d="M578.766,51.487v-0.895h-2.996H35.93h-2.996v0.895C15.272,52.701,2.095,66.753,0,83.808v3.002v355.724
c0,6.898,1.795,12.712,4.791,17.949c6.893,12.137,17.068,18.269,31.14,18.269h197.012v49.695h-37.425
c-9.281,0-16.467,7.218-16.467,16.48c0,9.262,7.186,16.479,16.467,16.479h220.666c9.281,0,16.768-7.218,16.768-16.479
c0-9.263-7.486-16.48-16.768-16.48h-37.425v-49.695H575.77c14.078,0,24.343-6.132,31.14-18.269
c3.085-5.493,5.091-11.37,5.091-17.949V86.811v-3.002C609.905,66.753,595.833,52.701,578.766,51.487z M578.766,86.811v355.724
c0,2.108-0.895,3.002-2.996,3.002H35.93c-2.095,0-2.996-0.894-2.996-3.002V86.811v-3.002h545.831V86.811z"/>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
-<g>
-</g>
</svg>
diff --git a/src/main.cpp b/src/main.cpp index 2a85617..6c7f492 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -20,23 +20,34 @@ bool showGui() { return _showGui; } bool backgroundMode() { return _backgroundMode; } } -static void parseCommandLine(const QApplication &a); +static void parseCommandLine(const QCoreApplication &a); int main(int argc, char *argv[]) { - QApplication a(argc, argv); + QCoreApplication *a; + bool gui = true; + for (int i = 0; i < argc; ++i) { + if (strcmp(argv[i], "-w") == 0 || strcmp(argv[i], "--wakeup") == 0) { + gui = false; + } + } + if (gui) { + a = new QApplication(argc, argv); + } else { + a = new QCoreApplication(argc, argv); + } QCoreApplication::setApplicationName("BeamerGUI XP - Home Edition"); QCoreApplication::setApplicationVersion("2.0"); // System strings - QTranslator *qtTranslator = new QTranslator(&a); + QTranslator *qtTranslator = new QTranslator(a); qtTranslator->load(QLocale::system(), "qt", "_", QLibraryInfo::location(QLibraryInfo::TranslationsPath)); - a.installTranslator(qtTranslator); + a->installTranslator(qtTranslator); // App specific - QTranslator *translator = new QTranslator(&a); + QTranslator *translator = new QTranslator(a); translator->load(QLocale::system(), ":"); - a.installTranslator(translator); + a->installTranslator(translator); - parseCommandLine(a); + parseCommandLine(*a); if (_wakeup) { return Bus::inst()->registerService() ? 0 : 1; @@ -60,10 +71,10 @@ int main(int argc, char *argv[]) } if (w == nullptr) return 0; - return a.exec(); + return a->exec(); } -static void parseCommandLine(const QApplication &a) +static void parseCommandLine(const QCoreApplication &a) { // Command line QCommandLineParser parser; diff --git a/src/widget.cpp b/src/widget.cpp index bc4869f..49e5285 100644 --- a/src/widget.cpp +++ b/src/widget.cpp @@ -68,6 +68,9 @@ static void addBoldListener(QComboBox *combo) }); } +static const QString resetButtonStyle("padding: 2px; margin: 0 0 1px 1px;"); +static const QString resetButtonHotStyle(resetButtonStyle + "background-color: #f99;"); + /* * Main widget */ @@ -77,64 +80,92 @@ Widget::Widget(QWidget *parent) : _ui(new Ui::Widget), _popupCount(0), _iProjector(QIcon(":projector")), - _iScreen(QIcon(":screen")) + _iScreen(QIcon(":screen")), + _lastScreenCount(0), + _isDbus(true) { _ui->setupUi(this); + // Add refresh button + _ui->btnReload->setStyleSheet(resetButtonStyle); + _ui->tabWidget->setCornerWidget(_ui->btnReload); + connect(_ui->btnReload, &QPushButton::clicked, [=](bool) { + _ui->btnReload->setStyleSheet(resetButtonStyle); + ScreenSetup::inst()->initModes(); + initControls(); + }); setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint); QTimer *top = new QTimer(this); connect(top, &QTimer::timeout, [=]() { + if (this->isHidden()) + return; // Move window to current screen - QRect win = this->geometry(); - QPoint cursor = QCursor::pos(); - const QScreen *winScreen = nullptr, *mouseScreen = nullptr; - //qDebug() << "Mouse at" << cursor << " window at" << win; - for (auto screen : _qtScreens) { - QRect geo = screen->geometry(); - if (geo.contains(win)) { - winScreen = screen; - //qDebug() << "Window on screen" << geo; - } - if (geo.contains(cursor)) { - mouseScreen = screen; - //qDebug() << "Mouse on screen" << geo; - } - } - if (mouseScreen != nullptr && mouseScreen != winScreen) { - QPoint offset = mouseScreen->geometry().topLeft(); - QSize spacing = (mouseScreen->size() - this->size()) / 2; - offset.rx() += spacing.width(); - offset.ry() += spacing.height(); - this->move(offset); + if (qApp->mouseButtons() == Qt::NoButton) { + updateWindowPlacement(); } // Raise window if appropriate - if (_popupCount > 0) - return; - auto combos = this->findChildren<QComboBox*>(); - for (auto combo : combos) { - if (combo->view()->isVisible()) - return; + if (_popupCount == 0) { + bool ok = true; + auto combos = this->findChildren<QComboBox*>(); + for (auto combo : combos) { + if (combo->view()->isVisible()) { + ok = false; + break; + } + } + if (ok) { + raise(); + } } - raise(); }); top->start(1000); addBoldListener(_ui->cboCloneResolution); addBoldListener(_ui->cboDualLeft); addBoldListener(_ui->cboDualRight); connectButtons(); - auto fun = [this](const QScreen *scrn) { - qDebug() << "QT SEES SCREEN" << scrn->geometry(); - _qtScreens.append(scrn); - /* - connect(scrn, &QScreen::geometryChanged, [this](const QRect &geom) { - - }); - */ + // Handle screens on Qt level + // Timer + QTimer *t = new QTimer(this); + t->setSingleShot(true); + // Worked fine + connect(t, &QTimer::timeout, [=]() { + int currentCount = ScreenSetup::inst()->queryCurrentOutputCount(); + qDebug() << "Timeout. Dbus:" << _isDbus << "old count:" << _lastScreenCount << "new count:" << currentCount; + if (_isDbus && _lastScreenCount >= currentCount) + return; // Ignore dbus events if the screen count didn't change (or decreased) + if (this->isHidden()) { + ScreenSetup::inst()->initModes(); + _isDbus = true; + this->show(); + } else { + if (currentCount > _lastScreenCount) { + ScreenSetup::inst()->initModes(); + initControls(); + } else { + _ui->btnReload->setStyleSheet(resetButtonHotStyle); + } + } + }); + auto popupGui = [=]() { + if (this->isHidden()) { + t->start(1000); + } else { + _ui->btnReload->setStyleSheet(resetButtonHotStyle); + } }; for (auto scrn : QGuiApplication::screens()) { - fun(scrn); + _qtScreens.append(scrn); } - connect(qApp, &QGuiApplication::screenAdded, fun); - connect(qApp, &QGuiApplication::screenRemoved, [this](const QScreen *scrn) { + connect(qApp, &QGuiApplication::screenAdded, [=](const QScreen *scrn) { + qDebug() << "QT SEES SCREEN" << scrn->geometry(); + _qtScreens.append(scrn); + if (CommandLine::backgroundMode()) { + qDebug() << "Qt setting FALSE"; + _isDbus = false; + popupGui(); + } + }); + connect(qApp, &QGuiApplication::screenRemoved, [=](const QScreen *scrn) { + qDebug() << "Qt lost screen" << scrn->geometry(); _qtScreens.removeAll(scrn); }); _ui->btnExit->setVisible(CommandLine::backgroundMode()); @@ -144,28 +175,23 @@ Widget::Widget(QWidget *parent) : qDebug() << "WARNING: CANNOT CONNECT TO DBUS FOR LISTENING"; // TODO: GUI feedback } else { - // Worked fine - // Timer - QTimer *t = new QTimer(this); - t->setSingleShot(true); - connect(t, &QTimer::timeout, [=]() { - if (this->isHidden()) { - ScreenSetup::inst()->initModes(); - this->show(); - } else { - // TODO: Flash button - } - }); // GUI popup logic connect(Bus::inst(), &Bus::serviceConnected, [=]() { qDebug() << "\\o/ Received DBus connect notification \\o/"; - if (this->isHidden()) { - t->start(1500); - } else { - // TODO: Flash button - } + popupGui(); }); } + // Xlib + connect(ScreenSetup::inst(), &ScreenSetup::outputConfigChanged, [=](ConnectionEvent type) { + if (type == ConnectionEvent::Disconnected) { + if (!this->isHidden()) { + _ui->btnReload->setStyleSheet(resetButtonHotStyle); + } + } else { + _isDbus = false; + popupGui(); + } + }); } } @@ -178,6 +204,7 @@ void Widget::showEvent(QShowEvent *event) { QWidget::showEvent(event); initControls(true); + updateWindowPlacement(true); } static void fillCombo(QComboBox *combo, const ResolutionVector &resolutions, const QSize &preselected, const QSize &preferred = QSize()) @@ -223,6 +250,8 @@ void Widget::comboBold(int index) void Widget::initControls(bool jumpToTab) { + _lastScreenCount = ScreenSetup::inst()->getOutputCount(); + _ui->btnReload->setStyleSheet(resetButtonStyle); // ScreenMode currentOpMode = ScreenSetup::inst()->getCurrentMode(); _ui->tabWidget->setTabEnabled(1, CommandLine::testMode() || ScreenSetup::inst()->getOutputCount() == 2 || currentOpMode == ScreenMode::Dual); @@ -265,13 +294,24 @@ void Widget::initControls(bool jumpToTab) QSize preferredClone; for (auto screen : screenList) { if (!screen.preferredResolution.isEmpty()) { - preferredClone = screen.preferredResolution; + if (screen.isProjector // Projector always overrides + || preferredClone.isEmpty() // Have no preferred yet + || screen.preferredResolution.width() < preferredClone.width() // For normal screens, + || screen.preferredResolution.height() < preferredClone.height()) { // smallest wins + preferredClone = screen.preferredResolution; + } if (screen.isProjector) break; } } fillCombo(_ui->cboCloneResolution, modes, screenList[0].currentResolution, preferredClone); // Dual + _ui->dualContainer->takeAt(0); + _ui->dualContainer->takeAt(1); + _ui->dualContainer->takeAt(2); + _ui->dualContainer->addLayout(_ui->dualLeft); + _ui->dualContainer->addWidget(_ui->btnDualSwap); + _ui->dualContainer->addLayout(_ui->dualRight); if (screenList.size() >= 2) { auto lists = QList<QComboBox*>({_ui->cboDualLeft, _ui->cboDualRight}); int j = 0; @@ -575,4 +615,30 @@ void Widget::connectButtons() { } +void Widget::updateWindowPlacement(bool force) +{ + QRect win = this->geometry(); + QPoint cursor = QCursor::pos(); + const QScreen *winScreen = nullptr, *mouseScreen = nullptr; + //qDebug() << "Mouse at" << cursor << " window at" << win; + for (auto screen : _qtScreens) { + QRect geo = screen->geometry(); + if (geo.contains(win)) { + winScreen = screen; + //qDebug() << "Window on screen" << geo; + } + if (geo.contains(cursor)) { + mouseScreen = screen; + //qDebug() << "Mouse on screen" << geo; + } + } + if (mouseScreen != nullptr && (force || mouseScreen != winScreen)) { + QPoint offset = mouseScreen->geometry().topLeft(); + QSize spacing = (mouseScreen->size() - this->size()) / 2; + offset.rx() += spacing.width(); + offset.ry() += spacing.height(); + this->move(offset); + } +} + //////////////////////////////////////////////////////////////////////////////// diff --git a/src/widget.h b/src/widget.h index f784ce9..ddf14fb 100644 --- a/src/widget.h +++ b/src/widget.h @@ -34,10 +34,13 @@ private: QList<const QScreen *> _qtScreens; bool _popupCount; QIcon _iProjector, _iScreen; + int _lastScreenCount; + bool _isDbus; void initControls(bool jumpToTab = false); void connectButtons(); bool keepResolution(); + void updateWindowPlacement(bool force = false); }; #endif // WIDGET_H diff --git a/src/widget.ui b/src/widget.ui index a0be031..fe6f67b 100644 --- a/src/widget.ui +++ b/src/widget.ui @@ -22,11 +22,28 @@ </property> <layout class="QHBoxLayout" name="horizontalLayout_2"> <item> + <widget class="QPushButton" name="btnReload"> + <property name="text"> + <string notr="true"/> + </property> + <property name="icon"> + <iconset resource="icons.qrc"> + <normaloff>:/refresh</normaloff>:/refresh</iconset> + </property> + <property name="iconSize"> + <size> + <width>16</width> + <height>16</height> + </size> + </property> + </widget> + </item> + <item> <layout class="QVBoxLayout" name="verticalLayout_6"> <item> <widget class="QTabWidget" name="tabWidget"> <property name="currentIndex"> - <number>2</number> + <number>1</number> </property> <widget class="QWidget" name="tabClone"> <attribute name="title"> @@ -109,7 +126,7 @@ border: 2px solid black; <item> <layout class="QHBoxLayout" name="dualContainer"> <item> - <layout class="QVBoxLayout" name="verticalLayout_2"> + <layout class="QVBoxLayout" name="dualLeft"> <item> <widget class="QWidget" name="wDualLeft" native="true"> <layout class="QVBoxLayout" name="verticalLayout_4"> @@ -152,7 +169,7 @@ border: 2px solid black; </widget> </item> <item> - <layout class="QVBoxLayout" name="verticalLayout_3"> + <layout class="QVBoxLayout" name="dualRight"> <item> <widget class="QWidget" name="wDualRight" native="true"> <property name="margin" stdset="0"> diff --git a/src/xprivate.cpp b/src/xprivate.cpp index 120fb66..ce7ce8c 100644 --- a/src/xprivate.cpp +++ b/src/xprivate.cpp @@ -82,9 +82,11 @@ void XPrivate::updateScreenResources() _modeMap.insert( _screenResources->modes[i].id, &_screenResources->modes[i]); + /* qDebug() << _screenResources->modes[i].id << "\t" << _screenResources->modes[i].width << "x" << _screenResources->modes[i].height; + */ } /* @@ -190,25 +192,23 @@ void XPrivate::updateScreenResources() qSort(screens.begin(), screens.end(), xRectLessThan); int endX = -0xffff; qDebug() << "From left to right"; - for (OutputInfo* output : screens) { - if (output->mode == nullptr) { - qDebug() << "(Ignoring" << output->outputName << "since it's disconnected)"; - } - 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); - } - } + for (OutputInfo* output : screens) { + if (output->mode == nullptr) { + qDebug() << "(Ignoring" << output->outputName << "since it's disconnected)"; + continue; + } + if (output->crtc->x >= endX) { + QSize res(0, 0); + res = QSize(int(output->mode->width), int(output->mode->height)); + _resolutions.append(res); + endX = -0xffff; // Reset + } + output->position = _resolutions.size() - 1; + qDebug() << "Screen (" << output->crtc->x << "," << output->crtc->y << ") @" << output->mode->width << "x" << output->mode->height << "as screen" << output->position; + if (output->crtc->x + int(output->crtc->width) > endX) { + endX = output->crtc->x + int(output->mode->width); + } + } qDebug() << "Loaded."; } @@ -2,7 +2,7 @@ #include "xprivate.h" #include "cvt.h" #include <QDebug> - +#include <QSocketNotifier> /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// @@ -36,10 +36,58 @@ static ScreenInfo initScreenInfo(const OutputInfo *oi, const ModeMap &om) ScreenSetup * ScreenSetup::_instance = nullptr; +static int errorHandlerX(Display*) +{ + exit(1); +} + ScreenSetup::ScreenSetup() : a(new XPrivate()) { - /* Get informations about Xserver */ + int event_base_return, error_base_return; + if (!XRRQueryExtension(a->_display, &event_base_return, &error_base_return)) { + qDebug() << "No XRANDR extension found"; + exit(1); + } updateScreenResources(); + //XSelectInput(a->_display, DefaultRootWindow(a->_display), StructureNotifyMask); + XRRSelectInput(a->_display, DefaultRootWindow(a->_display), RROutputChangeNotifyMask); + //XSync(a->_display, False); + XSetIOErrorHandler((XIOErrorHandler) errorHandlerX); + _socketNotifier = new QSocketNotifier(qintptr(ConnectionNumber(a->_display)), QSocketNotifier::Read); + connect(_socketNotifier, &QSocketNotifier::activated, [=](int) { + XEvent ev; + qDebug() << "Socket Event"; + while (XPending(a->_display) > 0) { + XNextEvent(a->_display, &ev); + if (ev.type - event_base_return != RRNotify) { + qDebug() << "Received unknown X event"; + continue; + } + qDebug() << "Got Change Event"; + XRROutputChangeNotifyEvent *oce = reinterpret_cast<XRROutputChangeNotifyEvent*>(&ev); + XRRScreenResources *sr = XRRGetScreenResources(oce->display, oce->window); + if (sr == nullptr) { + emit outputConfigChanged(ConnectionEvent::Unknown); + continue; + } + XRROutputInfo *oi = XRRGetOutputInfo(a->_display, sr, oce->output); + if (oi == nullptr) { + XRRFreeScreenResources(sr); + emit outputConfigChanged(ConnectionEvent::Unknown); + continue; + } + if (oi->connection == RR_Connected) { + emit outputConfigChanged(ConnectionEvent::Connected); + } else if (oi->connection == RR_Disconnected) { + emit outputConfigChanged(ConnectionEvent::Disconnected); + } else { + emit outputConfigChanged(ConnectionEvent::Unknown); + } + XRRFreeOutputInfo(oi); + XRRFreeScreenResources(sr); + } + }); + } @@ -337,11 +385,36 @@ ResolutionVector ScreenSetup::getCommonModes() const return ret; } +/** + * Return number of connected (no active) outputs according to last query + */ int ScreenSetup::getOutputCount() const { return a->_outputMap.size(); } +/** + * Query currently connected number of outputs + */ +int ScreenSetup::queryCurrentOutputCount() const +{ + auto sr = XRRGetScreenResourcesCurrent(a->_display, DefaultRootWindow(a->_display)); + if (sr == nullptr) + return 0; + int count = 0; + for (int i = 0; i < sr->noutput; ++i) { + XRROutputInfo* info = XRRGetOutputInfo(a->_display, sr, sr->outputs[i]); + if (info == nullptr) + continue; + if (info->connection != RR_Disconnected) { + count++; + } + XRRFreeOutputInfo(info); + } + XRRFreeScreenResources(sr); + return count; +} + const ResolutionVector &ScreenSetup::getVirtualResolutions() const { return a->_resolutions; @@ -7,6 +7,7 @@ struct ScreenInfo; class XPrivate; +class QSocketNotifier; typedef QVector<QSize> ResolutionVector; @@ -31,8 +32,16 @@ struct ScreenInfo ResolutionVector modes; }; -class ScreenSetup +enum class ConnectionEvent { + Connected, + Disconnected, + Unknown, +}; + +class ScreenSetup : public QObject +{ + Q_OBJECT public: void updateScreenResources(); void initModes(); @@ -44,6 +53,7 @@ public: bool setCustom(const QList<QPair<QSize, QList<QString>>> &list); ResolutionVector getCommonModes() const; int getOutputCount() const; + int queryCurrentOutputCount() const; QMap<QString, ScreenInfo> getScreenPositions() const; const ResolutionVector &getVirtualResolutions() const; @@ -59,6 +69,10 @@ private: static ScreenSetup * _instance; XPrivate *a; + QSocketNotifier *_socketNotifier; +signals: + void outputConfigChanged(ConnectionEvent event); + }; /////////////////////////////////////////////////////////////////////////// diff --git a/udev/99-beamergui.rules b/udev/99-beamergui.rules new file mode 100644 index 0000000..06364e2 --- /dev/null +++ b/udev/99-beamergui.rules @@ -0,0 +1,3 @@ +# Tell beamergui to show up +# +SUBSYSTEM=="drm", ACTION=="change", RUN+="/opt/openslx/bin/beamergui -w" |