Merge lp:~vanvugt/unity/fix-742544-trunk into lp:unity

Proposed by Daniel van Vugt on 2011-10-16
Status: Work in progress
Proposed branch: lp:~vanvugt/unity/fix-742544-trunk
Merge into: lp:unity
Diff against target: 148 lines (+91/-21)
1 file modified
plugins/unityshell/src/UScreen.cpp (+91/-21)
To merge this branch: bzr merge lp:~vanvugt/unity/fix-742544-trunk
Reviewer Review Type Date Requested Status
Sam Spilsbury (community) 2011-10-16 Needs Fixing on 2011-12-19
Review via email: mp+79492@code.launchpad.net

Description of the change

Fixed: Launcher/dash ignoring primary monitor setting. (LP: #742544)

The primary monitor according to Unity is now chosen in this order:
  1. If there is only one monitor, then it's obvious.
  2. Primary monitor as set in the Displays control panel (Xrandr).
  3. Primary monitor as defined by xorg option TwinViewXineramaInfoOrder
     used by NVIDIA.
  4. Laptop screen, if one can be found.
  5. Left-most monitor.

To post a comment you must log in.
Andrea Azzarone (azzar1) wrote :

Of course the "preview" in display settings panel should use the same algorithm. I will do it fixing Bug #875644.

Daniel van Vugt (vanvugt) wrote :

Work in progress... needs fixing for systems that don't support Xrandr and use Xinerama instead (NVIDIA). See comment #98 of bug 742544.

lp:~vanvugt/unity/fix-742544-trunk updated on 2011-11-04
1724. By Daniel van Vugt on 2011-11-04

Fix incorrect parsing of Option lines that don't start with spaces.

Daniel van Vugt (vanvugt) wrote :

Note that in the 5 steps described in this fix, steps 1-4 are already built into gdk_screen_get_primary_monitor().

If we are willing to remove step 5 (which I maintained for compatibility with the oneiric default behaviour), then most of UScreen::Refresh() can be deleted and replaced by: primary_ = gdk_screen_get_primary_monitor(screen).

Marco Trevisan (Treviño) (3v1n0) wrote :

Is this still a WIP? In that case, please, set the branch review status accordingly.
Thanks.

Daniel van Vugt (vanvugt) wrote :

Not WIP. The branch is ready for review and merging as is. And has been tested (from my PPA) by various users in the comments of bug 742544.

Sam Spilsbury (smspillaz) wrote :

18 +static bool
19 +XorgOptionDefined(const char *name)
20 +{
21 + bool found = false;
22 + FILE *file = fopen("/etc/X11/xorg.conf", "r");
23 + if (file != NULL)
24 + {
25 + char line[512];
26 + while (!found && fgets(line, sizeof(line), file) != NULL)
27 + {
28 + char option[128];
29 + if (sscanf(line, "Option%*[ \t]\"%127[^\"]\"", option) == 1 ||
30 + sscanf(line, "%*[ \t]Option%*[ \t]\"%127[^\"]\"", option) == 1)
31 + found = !strcmp(option, name);
32 + }
33 + fclose(file);
34 + }
35 + return found;
36 +}

That kind of scares me.

Is there a separate library that we can use to parse the xorg.conf or can you use nvidia-settings directly to do this ?

Probably best to add a unit test for stuff like this. I can ask tim if we can get someone else to do this.

Daniel van Vugt (vanvugt) wrote :

Scary, maybe. But well tested by many nvidia users already who use ppa:vanvugt/unity if you read the bug.

Also, calling nvidia-settings would be MUCH slower than this quick file scan.

I personally vote for my previous suggestion: Remove step 5 and then all the ugly changes can be deleted and replaced by:
primary_ = gdk_screen_get_primary_monitor(screen).

Sam Spilsbury (smspillaz) wrote :

I prefer option 5. There are probably other driver edge cases that gdk handles.

Also prefer to use nux::logging::Logger instead of g_print in unity. nux::logging::Logger allows filtering of error messages by domains etc and is safer than print* because it uses iostreams.

You can do that with something like:

namespace
{
    nux::logging::Logger logger ("unity.screen");
}

LOG_INFO (logger) << "foo!";

review: Needs Fixing
Daniel van Vugt (vanvugt) wrote :

"I prefer option 5."

Do you mean you agree with me and prefer removing step 5? This bug is _caused_ by step 5. :)

Sam Spilsbury (smspillaz) wrote :

Yes, I prefer removing step 5 and going with primary_ = gdk_screen_get_primary_monitor(screen)

Daniel van Vugt (vanvugt) wrote :

And if we do remove step 5 and revert to a simple:
primary_ = gdk_screen_get_primary_monitor(screen)
then the logging can be removed completely.

P.S. The glib functions do have domains, not to mention being faster and more elegant than nux. But anything is ;)...
http://developer.gnome.org/glib/2.30/glib-Message-Logging.html

Unmerged revisions

1724. By Daniel van Vugt on 2011-11-04

Fix incorrect parsing of Option lines that don't start with spaces.

1723. By Daniel van Vugt on 2011-11-04

Added detection of NVIDIA/TwinViewXinerama primary monitor settings.
The primary monitor according to Unity is now chosen in this order:
  1. If there is only one monitor, then it's obvious.
  2. Primary monitor as set in the Displays control panel (Xrandr).
  3. Primary monitor as defined by xorg option TwinViewXineramaInfoOrder
     used by NVIDIA.
  4. Laptop screen, if one can be found.
  5. Left-most monitor.
(LP: #742544)

1722. By Daniel van Vugt on 2011-10-16

Fixed: Launcher ignoring primary monitor setting. (LP: #742544)

The primary monitor according to Unity is now chosen in this order:
  1. Primary monitor if set in the Displays control panel (XRandR).
  2. Laptop screen, if one can be found.
  3. Left-most monitor.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'plugins/unityshell/src/UScreen.cpp'
2--- plugins/unityshell/src/UScreen.cpp 2011-09-23 23:53:13 +0000
3+++ plugins/unityshell/src/UScreen.cpp 2011-11-04 10:04:24 +0000
4@@ -17,6 +17,9 @@
5 */
6
7 #include "UScreen.h"
8+#include <X11/extensions/Xrandr.h>
9+
10+#define UPREFIX "UScreen: "
11
12 static UScreen* _default_screen = NULL;
13
14@@ -89,21 +92,41 @@
15 return FALSE;
16 }
17
18+static bool
19+XorgOptionDefined(const char *name)
20+{
21+ bool found = false;
22+ FILE *file = fopen("/etc/X11/xorg.conf", "r");
23+ if (file != NULL)
24+ {
25+ char line[512];
26+ while (!found && fgets(line, sizeof(line), file) != NULL)
27+ {
28+ char option[128];
29+ if (sscanf(line, "Option%*[ \t]\"%127[^\"]\"", option) == 1 ||
30+ sscanf(line, "%*[ \t]Option%*[ \t]\"%127[^\"]\"", option) == 1)
31+ found = !strcmp(option, name);
32+ }
33+ fclose(file);
34+ }
35+ return found;
36+}
37+
38 void
39 UScreen::Refresh()
40 {
41- GdkScreen* screen;
42- nux::Geometry last_geo(0, 0, 1, 1);
43-
44- screen = gdk_screen_get_default();
45-
46- _monitors.erase(_monitors.begin(), _monitors.end());
47-
48- g_print("\nScreen geometry changed:\n");
49+ GdkScreen* screen = gdk_screen_get_default();
50+
51+ _monitors.clear();
52+
53+ g_print("\n" UPREFIX "Screen geometry changed:\n");
54
55 int lowest_x = std::numeric_limits<int>::max();
56 int highest_y = std::numeric_limits<int>::min();
57- for (int i = 0; i < gdk_screen_get_n_monitors(screen); i++)
58+ int left_most = 0;
59+ int laptop_monitor = -1;
60+ int nmonitors = gdk_screen_get_n_monitors(screen);
61+ for (int i = 0; i < nmonitors; i++)
62 {
63 GdkRectangle rect = { 0 };
64
65@@ -111,24 +134,71 @@
66
67 nux::Geometry geo(rect.x, rect.y, rect.width, rect.height);
68
69- // Check for mirrored displays
70- if (geo == last_geo)
71- continue;
72- last_geo = geo;
73-
74 _monitors.push_back(geo);
75
76 if (geo.x < lowest_x || (geo.x == lowest_x && geo.y > highest_y))
77 {
78 lowest_x = geo.x;
79 highest_y = geo.y;
80- primary_ = i;
81- }
82-
83- g_print(" %dx%dx%dx%d\n", geo.x, geo.y, geo.width, geo.height);
84- }
85-
86- g_print("\n");
87+ left_most = i;
88+ }
89+
90+ gchar *name = gdk_screen_get_monitor_plug_name(screen, i);
91+ g_print(UPREFIX " #%d: %s %dx%d +%d+%d\n",
92+ i,
93+ name == NULL ? "?" : name,
94+ geo.width, geo.height,
95+ geo.x, geo.y);
96+ if (name)
97+ {
98+ if (g_ascii_strncasecmp(name, "LVDS", 4) == 0)
99+ laptop_monitor = i;
100+ g_free(name);
101+ }
102+ }
103+
104+ int gdk_primary = gdk_screen_get_primary_monitor(screen);
105+ if (nmonitors <= 1)
106+ {
107+ primary_ = 0;
108+ g_print(UPREFIX "Only one monitor.\n");
109+ }
110+ else if (gdk_primary > 0)
111+ {
112+ primary_ = gdk_primary;
113+ g_print(UPREFIX "Primary GDK monitor is unambiguously #%d.\n", primary_);
114+ }
115+ else // Primary setting is ambiguous. Need to check using other methods...
116+ {
117+ Display *dpy = nux::GetGraphicsDisplay()->GetX11Display();
118+ RROutput rr_primary = XRRGetOutputPrimary(dpy, DefaultRootWindow(dpy));
119+ if (rr_primary != None)
120+ {
121+ primary_ = 0;
122+ g_print(UPREFIX "Primary monitor is #0 according to GDK and Xrandr.\n");
123+ }
124+ else
125+ {
126+ g_print(UPREFIX "No primary monitor has been configured via Xrandr.\n");
127+ if (XorgOptionDefined("TwinViewXineramaInfoOrder"))
128+ {
129+ primary_ = gdk_primary;
130+ g_print(UPREFIX "Primary monitor is #%d according to GDK and "
131+ "confirmed by TwinViewXineramaInfoOrder.\n", primary_);
132+ }
133+ else if (laptop_monitor >= 0)
134+ {
135+ primary_ = laptop_monitor;
136+ g_print(UPREFIX "Using laptop LVDS display: #%d.\n", primary_);
137+ }
138+ else
139+ {
140+ primary_ = left_most;
141+ g_print(UPREFIX "Using left-most monitor: #%d.\n", primary_);
142+ }
143+ }
144+ }
145+ g_print(UPREFIX "Primary monitor for Unity is #%d.\n\n", primary_);
146
147 changed.emit(primary_, _monitors);
148 }