Linuxify a Microsoft Surface Pro

#linux

27 Apr 2019

Many users may need a big, sexy tablet in their everyday life (students, commuters, people with back pains...). However, someone - me included - just cannot stand the limits and annoyances of Windows. So why not turn your brand new Microsoft Surface in a blazing fast and perfectly capable linux machine?


Premise

(Skip to step 1 if you don’t care about my …)

When I started university a couple of months ago, I decided that I would forever abandon paper and penin favor of digital note taking on a tablet. After countless hours looking for a decent deal, I’ve decided to go for a Surface Pro 2017. It’s a great machine, but it has a tittle problem, and that’s Windows. Don’t get me wrong, the support that this OS offers is nice, but the problems I’ve encountered are countless (48 hours to update to build 1803, are you serious M$ ?!?). So I’ve decided that I want to have linux installed on it, in order to use for some proper work, first as a dual boot, maybe eventually I will completely ditch out Winzozz, but we’ll see. I’ve decided to go for linux mint just because I’m already familiar with it, but I might reconsider if any problem arise.

Code

You can find the script that I use in this tutorial on this github gist

(license?)

Step 1: installing linux

Luckily, this is pretty straight forward., but there are a couple of considerations to keep in mind.

That’s it! You have a fully functional linux machine, altough missing touch support and other things that make it a tablet, but follow below to make it a fully functional linux tablet.

Step 2: using a custom kernel

Step 3: small tweaks

Installing is rather easy, but to have a pleasant experience there’s still much work do to.

Changing DPI settings

As with every high resolution screen, DPI is a real problem, but I’ve found a couple of solution altough they don’t cover all my problems.

The first solution is to change the DPI settings for Cinnamon, so that at least every builtin program and desktop UI element is scaled. To do this I’ve installed teh package dconf-editor, which is a nice graphical editor for various configuration files. Once installed, navigate to the follwoing path: “/org/cinnamon/settings-daemon/plugins/xsettings/overrides”. In the custom value textbox you will see the setting for the DPI, the number is a bit weird, but to calculate it you need to multiply the desired dpi value by 1024, so to set a DPI of 256, you need to type in

{'Xft/DPI': <262144>}

(256*1024=262144)

However this will not work with everything. As you can see from the following screenshot, many applications (like wireshark) do not use this configuration and are pretty much impossible to use:

Wireshark screenshot

I’ve found a partial solution for this: check this script. It works by launching a graphical application and upscaling it, and even though it looks quite blurry, it’s still usable. However, it does have a problem, that is you shouldn’t run it as sudo (which you might need to do for some application) or you could face some problems (you simply won’t be able to login afterwards)

Using a proper stylus

Because linux and cinnamon are not particularly fond of multitouch support (not without some tweakings at least) in order to fully use your tablet you need to have some pointer device. Instead of going with the official microsoft pen though, I opted for a cheaper but fully compatible solution: this heiyo surface pen.

Out of the box it works fine as a pointer and mouse left-button, also the pressure sensor is available to software that supports it. However, it lacks a proper button mapping, but nothing that a bit of binding cannot solve. My particular pen has got two buttons: right click and eraser. Of course I would like to remap the right click to mouse right-click and possibly the eraser key to something like CTRL.

The first thing to do is to find the name and ID of the device, this is easily done by calling the command xinput. My output is the following:

⎡ Virtual core pointer                    	id=2	[master pointer  (3)]
⎜   ↳ Virtual core XTEST pointer              	id=4	[slave  pointer  (2)]
⎜   ↳ ipts 045E:001F UNKNOWN                  	id=7	[slave  pointer  (2)]
⎜   ↳ ipts 045E:001F Touchscreen              	id=9	[slave  pointer  (2)]
⎜   ↳ ipts 045E:001F Mouse                    	id=10	[slave  pointer  (2)]
⎜   ↳ ipts 045E:001F Pen Pen (0)              	id=18	[slave  pointer  (2)]
⎜   ↳ ipts 045E:001F Pen Eraser (0)           	id=19	[slave  pointer  (2)]
⎣ Virtual core keyboard                   	id=3	[master keyboard (2)]
    ↳ Virtual core XTEST keyboard             	id=5	[slave  keyboard (3)]
    ↳ Video Bus                               	id=6	[slave  keyboard (3)]
    ↳ ipts 045E:001F Pen                      	id=8	[slave  keyboard (3)]
    ↳ gpio-keys                               	id=11	[slave  keyboard (3)]
    ↳ gpio-keys                               	id=12	[slave  keyboard (3)]

Note that the ID will change depending on the order with which you connect the devices to the system, so if we want a permanent solution we need the name. In my case the pen name is ipts 045E:001F Pen Pen (0) and for some reason the eraser button is mapped to another device, ipts 045E:001F Pen Eraser (0).

Once you have the name, you need to know the button supported by your device and its mapping. To do this just run the command xinput list "ipts 045E:001F Pen Pen (0)" which at the beginning will tell me Buttons supported: 7. Then, to understand the mapping, just run xinput test "ipts 045E:001F Pen Pen (0)" and press the buttons to figure out how they are mapped. In my case the right-click button on the pen was button 2 and touching the screen with the tip was button 1. The eraser key did nothing because, as I mentioned before, it’s another device of its own.

To create a binding you need to use the command set-button-map for xinput which will bind the pen buttons to mouse buttons, the command that works for me is this:

xinput set-button-map "ipts 045E:001F Pen Pen (0)" 1 3 1 1 1 1 1

Every number is the corresponding mouse buttons to bind. 1 is mouse left-click, 2 is mouse middle-click, 3 is mouse right-click. In this case, I’m binding pen button 1 to mouse left-click, pen button 2 to mouse right-click and all the other unused buttons to mouse left-click.

Check my gist for the complete script.

Now, we need to find a way to start the script automatically, because clicking it every time the tablet turns on is rather annoying. Unfortunately I haven’t a found a solution for this yet :( You see, the issue is that it’s not enough to run the script as the button binding will not do anything until the device is actually used (the pen doesn’t even show for xinput until it touches the screen).

Multitouch gestures using touchegg

GTK3 touchscreen bug

Apparently in some version of the GTK3 library there is a bug and dropdown menu do not respond to touchscreen input, making some programs unusable without a pen (that works just fine as a pointer) or a mouse. GTK2 does not seem to be affected by this.

Waiting for a fix, I’ve found a workaround from the page for this issue on gitlab. Basically it’s a script that intercepts touchscreen event and translates them to mouse events. The script must be run as root on boot. You can download it from my gist that I’ve linked above. It should work out of the box but you might want to change the touchscreen device if you’re using a different model from me.

I know, it’s not the most elegant solution but it appears to work.

(run as root?)

Enabling hibernation/fixing suspension

Detecting screen rotation

Fixing grub2 bugs

(small text?)

On my version of grub2 there is a bug where the on screen keyboard will open and close every second and every time a key is pressed. This did not happen while I was installing from my USB stick so it must be some kind of regression. If you have this problem as well it should be enough to just install a slightly older version of grub.

Accessing BitLocker Windows partition

To access BitLocker you need dislocker a bitlocker driver for linux, just install it with apt (or your favourite package manager)

sudo apt install dislocker

Two mount points are neede, not just one so let’s create them

sudo mkdir /media/bitlocker
suod mkdir /media/bitlockermount

Then just ad.d these two lines to /etc/fstab. Change partition with the partition path Windows is installed to and password with the recovery password of BitLocker

<partition> /media/bitlocker fuse.dislocker recover-password=<password>,nofail 0 0
/media/bitlocker/dislocker-file /media/bitlockermount auto nofail 0 0

To get the recovery password you have to boot into windows and open the BitLocker management settings and from there, you can make a backup of your keys (which will just be a text file with a 48 number string and some instructions).

Step 4 (optional): move windows

If you value the tenths of GB that a Windows 10 installation takes on your disk, you can remove it. But you’ve paid a license for it, so why just get rid of it when you can virtualize it?

(is it legal?)

To do you need to have access to some information

You can get this information by running the script wininfo.ps1 from my gist.

Then create a virtual machine (I use virtualbox and I wouldn’t be able to give indication for other software) but do not add a disk yet

you can configure the rest as you like. Then run these two commands, where UUID and serial are the values we’ve found earlier with the script

vboxmanage modifyvm "Surface Pro 2017" --hardwareuuid <UUID>
VBoxManage setextradata "Surface Pro 2017" "VBoxInternal/Devices/ahci/0/Config/Port0/SerialNumber" <serial>

NOTE: for some reason, the UUID that windows read is different from the one that you give to VirtualBox; particularly, the first three groups of digits are inverted in order, so for example

e78918f9-4ed8-47c9-8006-db4cb5659487

would become

f91889e7-d84e-c947-8006-db4cb5659487

I know, it’s weird, I also wonder why, but that’s the way it is.

Now you need to get the disk out of your surface. Probably the easiest method is to just brutally copy your disk to an image and give it to VirtualBox. So I grabbed a 128GB sd card (the biggest and fastest storage I had) and copied a portion of the disk to it.

sudo dd if=/dev/nvme0n1 of=surface2017.img.1 bs=1G count=110

(Of course being the sd card as big as my disk I couldn’t just copy it in one passage, so we’ll need to get two image files and merge them togheter)

sudo dd if=/dev/nvme0n1 of=surface2017.img.2 bs=1G skip=110

Then, to merge the files

cat surface2017.img.1 surface2017.img.2 > surface2017.img

You can then mount the image as loop device and do whatever you want to do with it (remove the old linux partition, enlarge Windows’, etc.). The last thing to do is to convert the image to a .vdi disk and attach it to the VM.

VBoxManage convertdd surface2017.img surface2017.vdi --format VDI

That should be it to have a fully functional windows Virtual Machine. Have fun!

I suggest you read this blog post for further information on the virtualization process.

Last edit: 09/04/2019