Tuesday, May 01, 2018

Remote Ubuntu dev setup with multi screen VNC

Goal: A VM in the cloud or similar running an Ubuntu desktop with your dev tools (Sublime Text, Visual Studio Code, etc.), that you can remotely connect to and comfortably work on, spanning all your local monitors. Like this:

We'll use TigerVNC. RealVNC 4 that comes with Ubuntu 16.04 can also be used, but it lacks the X extensions that are required for a well behaved multi monitor setup. Also, Visual Studio Code can't run on RealVNC 4 without a special patched version of libxcb.so.1.

Ubuntu 18.04

Desktop edition

sudo apt update
sudo apt install --no-install-recommends openssh-server tigervnc-standalone-server tigervnc-common

sudo su
cat /etc/X11/Xvnc-session
mv /etc/X11/Xvnc-session /etc/X11/Xvnc-session.orig
cat > /etc/X11/Xvnc-session << "EOF"
#!/bin/sh
export GNOME_SHELL_SESSION_MODE=ubuntu
export XDG_CURRENT_DESKTOP=ubuntu:GNOME
vncconfig -nowin &
exec /etc/X11/Xsession
vncserver -kill $DISPLAY
EOF
chmod +x /etc/X11/Xvnc-session
exit

# Start the VNC session (this must be run as your user from an SSH session after each reboot)
vncserver

# To stop the VNC session
vncserver -kill

Server edition

sudo apt-get update
sudo apt install --no-install-recommends tigervnc-standalone-server tigervnc-common ubuntu-desktop

And then followed by the same procedure with /etc/X11/Xvnc-session and vncserver as for desktop.



For additional desktop software, I recommend using the Ubuntu Software Center, which will install Snap packages for popular desktop software.
# Install the Ubuntu Software Center
sudo apt install gnome-software


Ubuntu 16.04

I only tried with server edition on 16.04, so adapt as needed.
sudo apt-get update

sudo apt-get install --no-install-recommends ubuntu-desktop gnome-terminal unity-lens-applications unity-lens-files gnome-settings-daemon
sudo apt-get install --no-install-recommends software-center

wget https://bintray.com/tigervnc/stable/download_file?file_path=ubuntu-16.04LTS%2Famd64%2Ftigervncserver_1.8.0-1ubuntu1_amd64.deb -O tigervncserver_1.8.0-1ubuntu1_amd64.deb
sudo dpkg -i tigervncserver_1.8.0-1ubuntu1_amd64.deb
sudo apt-get install -f

mkdir $HOME/.vnc/
cat > $HOME/.vnc/xstartup <<"EOF"
#!/bin/sh
unset SESSION_MANAGER
unset DBUS_SESSION_BUS_ADDRESS
/etc/X11/xinit/xinitrc &
vncconfig -nowin &
/usr/lib/x86_64-linux-gnu/unity/unity-panel-service &
/usr/lib/x86_64-linux-gnu/indicator-datetime/indicator-datetime-service &
/usr/lib/x86_64-linux-gnu/indicator-keyboard/indicator-keyboard-service &
unity &
EOF
chmod +x $HOME/.vnc/xstartup

# some kde or qt apps seem to look ugly without this package
sudo apt-get install kde-style-breeze-qt4

# Start the VNC session (this must be run as your user from an SSH session after each reboot)
vncserver

# To stop the VNC session
vncserver -kill :1
For Additional desktop software, I recommend using Snap:
sudo apt install snap

# For example install Visual Studio Code, Chrome, and Sublime, like this
sudo snap install vscode --classic
sudo snap install chromium
sudo snap install sublime-text --classic

Connecting

Use the TigerVNC client to take advantage of the dynamic resizing and multi-screen features. Binaries for Win/Mac/Linux can be found on their website.

Open the TigerVNC menu by pressing F8, and note the shortcut keys here for full screen, minimize, etc. Copy the remote .vnc/passwd file locally and pass it to the TigerVNC client with the -PasswordFile= option to avoid having to type your password repeatedly. For example, I connect from my Windows box using a cmd file with this content:
start "" "C:\utils\vnc\vncviewer64-1.8.0.exe" "yourRemoteServer:5901" -PasswordFile=C:\utils\vnc\passwd


Secure connection

If you are connecting over the internet, you'll want to use an SSH tunnel to secure the unencrypted VNC. In that case you can start the VNC server in a mode where it only listens on localhost, and then use SSH port forwarding.
vncserver  -localhost=yes -nolisten tcp
Connect using your preferred SSH client, and set up port forwarding to localhost:5901, for example from your local port 5901.

Wednesday, November 22, 2017

Delete the undeletable on Windows

I had trouble deleting C:\ProgramData\Docker on Docker for Windows using Windows containers. This is because it contains a full Windows directory layout, with all system files and special permissions and flags. Here's a Powershell snippet to get a file hierarchy on Windows into a state where it can be deleted.

$ErrorActionPreference = "stop"

function reallyDelete($d) {
    function renameToNumbersRecursive($dir) {
        $i = 0
        foreach($f in (dir $dir)) {
            while(Test-Path ($dir + "\" + $i)) {
                $i++
            }
            try {
                Rename-Item $f.FullName ($dir + "\" + $i)
            } catch {
                takeown /f $dir | Out-Null
                icacls $dir /reset | Out-Null
                takeown /f $f.FullName | Out-Null
                icacls $f.FullName /reset | Out-Null
                Rename-Item $f.FullName ($dir + "\" + $i)
            }
        }

        foreach($f in (dir $dir)) {
            if($f -is [System.IO.DirectoryInfo]) {
                renameToNumbersRecursive($f.FullName)
            }
        }
    }

    # paths might become too long for icacls, so rename paths to short numbers
    renameToNumbersRecursive $d

    takeown /f $d /r   | Out-Null
    icacls $d /reset /T   | Out-Null

    attrib -s -h -r "$d\*.*" /s /d   | Out-Null

    cmd /c del /s /q $d   | Out-Null
    cmd /c rmdir /s /q $d   | Out-Null
}

reallyDelete "C:\ProgramData\Docker"

Tuesday, May 30, 2017

Utility to remind you to keep a task diary

Here's a program that asks you every hour what you are doing, so you end up with a diary at the end of the day. It saves your input to a text file WhatAmIDoing.txt in the same folder you put the exe.

The "ask every hour" button creates a Windows Scheduled Task. Note that if you move the location of the exe file, you will need to click the "ask every hour" button again.

https://acoby.com/utils/WhatAmIDoing.exe


Thursday, September 29, 2016

Mailing list manager

I needed a simple mailing list manager for a sports club I'm in. We wanted it to be of the type where an administrator manages the lists (not the type where any random person can subscribe him- or herself). Couldn't find any decent looking free service offering this, so I decided to make one: mailgroup.io .

These are its features:
  • Free for noncommercial use.
  • Your own custom domain.
  • Or @mailgroup.io as domain if you prefer.
  • Your choice of members-only mailing lists, or everyone-may-write.
  • Detailed Postfix delivery reports for each mail for each recipient.
  • Max 50 recipients per account (negotiable).
  • Mail triggers. These are special email addresses that trigger HTTP POSTs with the email content to a defined URL when they receive email. Useful if you want some logic on your website to be triggered by email.


Wednesday, September 14, 2016

GLaDOS-like sound pack for Taranis

Here's a soundpack for the FrSky Taranis, based on the original, but run through Melodyne so it sounds like GLaDOS from Portal: https://acoby.com/fpv/gladosStyleTaranisSounds.zip

Sunday, May 01, 2016

Heap sort implementation in C#

Here's a heap sort implementation in C#:

class HeapSort
{
    public void Sort(int[] a)
    {
        // Turn the array into a max heap
        for (int i = (a.Length / 2) - 1; i >= 0; i--)
        {
            MaxHeapify(a, i, a.Length);
        }

        // Remove the largest element from the max heap and put it on the end,
        // and then max heapify the heap which is now 1 smaller
        for (int i = a.Length - 1; i >= 1; i--)
        {
            Swap(a, 0, i);
            MaxHeapify(a, 0, i);
        }
    }

    private void MaxHeapify(int[] a, int i, int n)
    {
        int idxLeft  = (i + 1) * 2 - 1;
        int idxRight = (i + 1) * 2 - 1 + 1;

        int largest = i;

        if (idxLeft < n && a[idxLeft] > a[largest])
        {
            largest = idxLeft;
        }

        if (idxRight < n && a[idxRight] > a[largest])
        {
            largest = idxRight;
        }

        if (largest != i)
        {
            Swap(a, i, largest);
            MaxHeapify(a, largest, n);
        }
    }

    private void Swap(int[] a, int i, int j)
    {
        int tmp = a[i];
        a[i] = a[j];
        a[j] = tmp;
    }
}

Friday, March 25, 2016

Segment tree implementation in Java

Inspired by Kartik Kukreja's implementation, but storing all data in a tree node class, rather than an array. I find it a lot easier to comprehend this way.
class SegmentTreeNode {
    public int aggregateValue;
    public SegmentTreeNode left;
    public SegmentTreeNode right;
    public int origLowIndex;
    public int origHighIndex;
}

class SegmentTreeTest {

    public static SegmentTreeNode buildSegmentTree(int[] a, int low, int high) {
        SegmentTreeNode n = new SegmentTreeNode();
        n.origLowIndex = low;
        n.origHighIndex = high;

        if(low == high) {
            n.aggregateValue = a[low];
            return n;
        }

        int mid = (high - low)/2 + low;

        n.left = buildSegmentTree(a, low, mid);
        n.right = buildSegmentTree(a, mid+1, high);

        // This segment tree is for summation. Could also be min, max, or any other associative func.
        n.aggregateValue = n.left.aggregateValue + n.right.aggregateValue;

        return n;
    }

    public static SegmentTreeNode getAggregateValue(SegmentTreeNode n, int low, int high) {
        if(n.origLowIndex == low && n.origHighIndex == high) {
            return n;
        }

        // The interval is fully contained within the left node
        if(low >= n.left.origLowIndex && high <=  n.left.origHighIndex) {
            return getAggregateValue(n.left, low, high);
        }

        // The interval is fully contained within the right node
        if(low >= n.right.origLowIndex && high <=  n.right.origHighIndex) {
            return getAggregateValue(n.right, low, high);
        }

        // Split into queries on the left subtree and the right subtree
        SegmentTreeNode leftResult = getAggregateValue(n.left, low, n.left.origHighIndex);
        SegmentTreeNode rightResult = getAggregateValue(n.right, n.right.origLowIndex, high);
        SegmentTreeNode result = new SegmentTreeNode();
        result.origLowIndex = low;
        result.origHighIndex  = high;

        // This segment tree is for summation. Could also be min, max, or any other associative func.
        result.aggregateValue = leftResult.aggregateValue + rightResult.aggregateValue;

        return result;
    }

    public static void update(SegmentTreeNode n, int index, int val) {
        if(n.origLowIndex == index && n.origHighIndex == index) {
            n.aggregateValue = val;
            return;
        }

        if(n.left.origLowIndex <= index && index <= n.left.origHighIndex) {
            update(n.left, index, val);
        } else {
            update(n.right, index, val);
        }

        // This segment tree is for summation. Could also be min, max, or any other associative func.
        n.aggregateValue = n.left.aggregateValue + n.right.aggregateValue;
    }

    public static void main(String[] args) {
        //                    0  1  2  3  4  5  6  7  8   9  10
        int[] a = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 };
        SegmentTreeNode r = buildSegmentTree(a, 0, a.length-1);
        System.out.println(r.aggregateValue);

        System.out.println(getAggregateValue(r, 1, 3).aggregateValue);
        System.out.println(getAggregateValue(r, 4, 7).aggregateValue);

        update(r, 2, 10);
        System.out.println(r.aggregateValue);

        System.out.println(getAggregateValue(r, 4, 7).aggregateValue);
        System.out.println(getAggregateValue(r, 1, 3).aggregateValue);
    }
}