I love having a Linux VM kicking around, just in case I need it for something. It’s great for messing around in – and being a developer that dabbles in reverse engineering, Linux is far superior to Windows for the majority of the kind of stuff I do at home.
However, after putting a fresh Ubuntu 16.04 VM on one of my machines, I couldn’t seem to get the login screen, referred to as the “greeter”, to use the same resolution as when I was logged in. So of course, I scoured the internet in search of a cure.
I found lots of posts, dating back to almost a decade ago, relating to the same problem: the login screen using a much lower resolution than what a connected screen’s native resolution was. Unfortunately, all of them suggested the same fix, and I recalled attempting that fix once and having Ubuntu throw errors at me complaining about being in low graphics mode, basically preventing me from booting it. But, I decided to try it again anyway.
Ubuntu goes through a series of steps when it starts, and the ones specific to loading a graphics environment boil down to these simplified steps:
- Find all the configuration files for LightDM, the lightweight window manager used for the login screen
- Start LightDM, passing in all the configuration content found
- This is loaded in a specific order, and some values may be overridden by subsequent configs
- Start the login environment, which by default is the Unity Greeter
You can tell LightDM to use a specific resolution when it’s running, but this has to be done through a command line utility called xrandr. Essentially what we do is we give LightDM a command to run as part of its display setup, this command being a call to xrandr to set the resolution of a display. First, though, we have to find out which display we’re using. Opening up a terminal and just running it with no arguments dumps a list of all the displays currently connected:
Notice that for me I have a single screen, Screen 0, with 8 virtual displays, Virtual1 being the one currently used. Under Virtual1 is also a listing of the supported resolutions. There are two important markings for resolutions: a plus sign (+) indicates a preferred resolution, while an asterisk (*) indicates the selected resolution. This appears to be the smoking gun – at the login screen, I had a low resolution being used by default, and 800×600 is about the size that it looked to me.
So, the next thing to do was to craft a command to set the resolution to what I wanted: 1680×1050. Using the xrandr command, you need to specify which output you are modifying and then tell it what resolution to use. For me, it’s this:
~$ xrandr --output Virtual1 --primary --mode 1680x1050
- –output specifies which output device we are talking about
- –primary tells xrandr that we are setting this as a primary output, so it will be preferred in the future
- –mode specifies which resolution we are going to use
It’s important to run this command locally first, just to make sure it works. Xrandr is pretty picky and will exit with a non-zero return code if you mess it up, and if this happens during boot you’ll stop the graphics environment from loading – loads of fun. When you run this command you shouldn’t see anything happen, but at the most it’ll be a little flicker, unless your resolution is something different when you run it. If the command runs and you get no other output in the terminal, you’re good to go.
Next, I added this to the LightDM initialization. There’s a configuration option you can add to one of the files, called display-setup-script, which specifies a script to run when LightDM is starting up. You can either paste in the call to xrandr with the appropriate arguments here, or you can put it in a separate script and call that instead. Being a programmer, I opted for the latter, since that way I can reuse the script elsewhere. My script lives in the /usr/share/lightdm directory, named login-res.sh, and has the following contents:
#!/bin/sh xrandr --output Virtual1 --primary --mode 1680x1050
Of course I had to make this file executable, but that was easily done using chmod:
~$ sudo chmod a+x login-res.sh
LightDM keeps most of its configuration files in the directory /usr/share/lightdm/lightdm.conf.d, where each file is typically given a number prefix. The numbers specify the loading order – the lower the number, the higher the precedence. For example, a .conf file starting with 10- will be loaded before one starting with 20-. My directory looks like this:
Simple enough! I’ve got plenty of room before the 50s to inject my own script, and since we’re trying to affect the resolution of the login screen, I decided to name my config 20-unity-greeter.conf. Since this is a directory owned by root, I had to use sudo to make a file with that name, with the following contents:
Done! Write, quit, sudo reboot!
Crap. It didn’t work. I figured something was perhaps changing the resolution further down the line in configurations, so I moved the display-setup-script option to 50-xserver-command to ensure it’s one of the last things run. After doing that, I saw the resolution I wanted being used for just a split second before getting changed back to the ugly one.
Most things log to a file in Linux, so I took a look in /var/log/ to see if there was anything interesting going on. Not really – although I noticed that LightDM was actually the thing responsible for kicking of the unity greeter, which is the login environment. So I thought perhaps there was something I needed to tell it as well such that I could have my preferred resolution.
I found the project site, pulled down the source code, and started reading through it. It’s written in something called vala, which I’ve never heard of, but interestingly enough it’s apparently written in a C#-like syntax, which then generates C code, a clever way to get around the cross-platform problem. After locating the main method and then following it around to see what was going on, I came across these lines:
var screen = get_screen (); screen.monitors_changed.connect (monitors_changed_cb); monitors_changed_cb (screen);
Ok… well it’s clear it is figuring out what to use for a resolution. Where does the resolution come from? Further down…
resize (screen.get_width (), screen.get_height ());
So here’s a potential culprit – it appears the greeter might be getting a default resolution for the primary screen, which could just be the “preferred” resolution according to the device. In my case, that being 800×600 if you remember the dump I got from xrandr. It turns out there is a bug logged against this issue for unity-greeter, so I’ll have to wait for that.
However, there’s another solution we can use – copying the monitors.xml configuration. LightDM stores the configured monitors in an XML file, located in ~/.config/, which specifies the layout and set resolution of displays. If your environment after login is correct, like me, you’re in luck – you can just copy that file to another directory that LightDM reads from, and it should use the same layouts. In the world of Linux, though, it’s probably better to use a link, so if we ever change our resolution it’ll be reflected at the login screen. You can do this by issuing the following command:
~$ sudo ln ~/.config/monitors.xml /var/lib/lightdm/.config/monitors.xml
Apparently symbolic links do not work, hence the absence of a -s.
Finally, after doing this, everything worked and had the correct resolution. No more resizing of the desktop background image upon login, no more crappy looking login screen. All fixed!
I hope this helps someone else; this problem has been annoying me for a while.