/// <summary>
        /// Updates the connection status page before rendering it.
        /// </summary>
        /// <param name="sender">Page being updated</param>
        /// <param name="e">Update event arguments</param>
        private static void UpdateConnectionStatusPage(object sender, UpdateEventArgs e)
        {
            LcdGdiPage         page             = (LcdGdiPage)sender;
            LcdGdiImage        refreshButton    = (LcdGdiImage)page.Children[0];
            LcdGdiImage        backButton       = (LcdGdiImage)page.Children[1];
            LcdGdiScrollViewer deviceScrollView = (LcdGdiScrollViewer)page.Children[3];
            LcdGdiText         deviceText       = (LcdGdiText)deviceScrollView.Child;
            LcdGdiText         statusText       = (LcdGdiText)page.Children[4];

            if (oath != null && oath.HasChallenge())
            {
                using (var dialog = new PasswordDialog())
                {
                    dialog.OKButton.Click += (_sender, _e) =>
                    {
                        try
                        {
                            oath.Validate(dialog.PasswordBox.Text);
                            statusText.Text = "Connected.";
                            CreateTimer("ConnectionStatusSwitchPage", 2);
                        }
                        catch (UnexpectedResponseException)
                        {
                            statusText.Text = "Invalid password, try again...";
                        }
                        dialog.Close();
                    };
                    dialog.CancelButton.Click += (_sender, _e) =>
                    {
                        oath = null;
                        ccid = null;
                        page.Device.Pages[0].SetAsCurrentDevicePage();
                        dialog.Close();
                    };
                    dialog.ShowDialog();
                }
            }

            if (CheckTimer("ConnectionStatusSwitchPage"))
            {
                // Update list items
                lock (entriesList.Lock)
                {
                    entriesList.Items   = oath.List();
                    entriesList.Changed = true;

                    // Calculate all keys and update touch info
                    var codes = oath.CalculateAll();
                    foreach (var code in codes)
                    {
                        if (code.Credential.Touch)
                        {
                            var idx = entriesList.Items.FindIndex(item => item.Name.Equals(code.Credential.Name));
                            if (idx != -1)
                            {
                                var item = entriesList.Items[idx];
                                item.Touch             = code.Credential.Touch;
                                entriesList.Items[idx] = item;
                            }
                        }
                    }
                }

                // Switch page
                page.Device.Pages[2].SetAsCurrentDevicePage();
                return;
            }

            lock (devicesList.Lock)
            {
                while (actions.TryTake(out Action a))
                {
                    switch (a)
                    {
                    case Action.Connect:
                        try
                        {
                            ccid                    = CCIDDriver.OpenDevice(devicesList.Items[devicesList.Selected]);
                            oath                    = new OATHController(ccid);
                            deviceText.Text         = devicesList.Items[devicesList.Selected];
                            backButton.IsVisible    = false;
                            refreshButton.IsVisible = false;

                            if (oath.HasChallenge())
                            {
                                statusText.Text = "Waiting for password...";
                            }
                            else
                            {
                                statusText.Text = "Connected.";
                                CreateTimer("ConnectionStatusSwitchPage", 2);
                            }
                        }
                        catch (ConnectionException)
                        {
                            oath                    = null;
                            ccid                    = null;
                            deviceText.Text         = devicesList.Items[devicesList.Selected];
                            statusText.Text         = "Unable to connect.";
                            backButton.IsVisible    = true;
                            refreshButton.IsVisible = true;
                        }
                        break;
                    }
                }
            }
        }
        /// <summary>
        /// Updates the device selection page before rendering it.
        /// </summary>
        /// <param name="sender">Page being updated</param>
        /// <param name="e">Update event arguments</param>
        private static void UpdateDevicePage(object sender, UpdateEventArgs e)
        {
            LcdGdiPage page = (LcdGdiPage)sender;

            lock (devicesList.Lock)
            {
                // Update list items
                string[] newItems = CCIDDriver.ListReaders();
                if (!newItems.Equals(devicesList.Items))
                {
                    devicesList.Changed = true;
                }

                if (!devicesList.Changed)
                {
                    return;
                }

                page.Children.Clear();
                devicesList.Items = newItems;

                if (devicesList.Items == null || devicesList.Items.Length == 0)
                {
                    page.Children.Add(new LcdGdiText
                    {
                        Text   = "No YubiKey detected.",
                        Margin = new MarginF(0.0f, 0.0f, 0.0f, 0.0f),
                        Font   = new Font(FontFamily.GenericSansSerif, 7.0f, FontStyle.Underline)
                    });
                }
                else
                {
                    // Update list selected state based on new items
                    while (devicesList.Selected >= devicesList.Items.Length && devicesList.Selected > 0)
                    {
                        devicesList.Selected--;
                    }

                    // Add button icons
                    page.Children.Add(new LcdGdiImage
                    {
                        Image  = Properties.Resources.ArrowUP,
                        Margin = new MarginF(12.0f, 33.0f, 0.0f, 0.0f)
                    });
                    page.Children.Add(new LcdGdiImage
                    {
                        Image  = Properties.Resources.ArrowDN,
                        Margin = new MarginF(55.0f, 33.0f, 0.0f, 0.0f)
                    });
                    page.Children.Add(new LcdGdiImage
                    {
                        Image  = Properties.Resources.Check,
                        Margin = new MarginF(97.0f, 33.0f, 0.0f, 0.0f)
                    });

                    // Add list items
                    for (int i = devicesList.Offset, j = 0; i < devicesList.Items.Length && j < 3; i++, j++)
                    {
                        if (i == devicesList.Selected)
                        {
                            page.Children.Add(new LcdGdiRectangle(Brushes.Black, new RectangleF(0.0f, j * 11.0f, 161.0f, 12.0f)));
                        }

                        page.Children.Add(new LcdGdiScrollViewer
                        {
                            Child = new LcdGdiText
                            {
                                Text  = devicesList.Items[i],
                                Brush = i == devicesList.Selected ? Brushes.White : Brushes.Black
                            },
                            Margin = new MarginF(0.0f, j * 11.0f, 0.0f, 0.0f),
                            HorizontalAlignment = LcdGdiHorizontalAlignment.Stretch,
                            VerticalAlignment   = LcdGdiVerticalAlignment.Stretch,
                            AutoScrollX         = i == devicesList.Selected
                        });
                    }
                }
            }
        }