#!/usr/bin/python3

import gi

gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, GLib, Gdk
import os
from os.path import expanduser
import getpass
import pwd
import re
import resource
import shutil
import shlex
import signal
import subprocess
import glob
import dbus
from dbus.mainloop.glib import DBusGMainLoop
import time
import jack
import grp
import json
import copy

DBusGMainLoop(set_as_default=True)


class SysInfo:
    """Get information about the system"""

    # get info about if rtaccess is setup right
    def user_audio(self):
        """Checks if current user is in the audio group, or not"""
        audio_users = []
        audio_users = grp.getgrnam("audio")[3]
        user = getpass.getuser()
        audio_group = False
        if user in audio_users:
            audio_group = True

        return audio_group

    def check_pam_files(self):
        '''Checks for the existence of two files'''
        jack_file_exists = False
        if os.path.isfile("/etc/security/limits.d/audio.conf"):
            jack_file_exists = True
        return jack_file_exists

    def check_rlimits(self):
        """returns hard rlimit values for RTPRIO and MEMLOCK"""
        return resource.getrlimit(resource.RLIMIT_RTPRIO)[1], resource.getrlimit(resource.RLIMIT_MEMLOCK)[1]

    # System tweaks
    def get_performance(self):
        '''Checks for current cpu governor'''
        in_performance = False
        if os.path.isfile("/sys/devices/system/cpu/cpufreq/policy0/scaling_governor"):
            with open("/sys/devices/system/cpu/cpufreq/policy0/scaling_governor", "r") as perform_file_test:
                for line in perform_file_test:
                    if re.match("performance", line.rstrip()):
                        in_performance = True
        return in_performance

    def get_boosted(self):
        '''Checks for Intel boost state'''
        boosted = False
        boost_path = "/sys/devices/system/cpu/intel_pstate/no_turbo"
        if os.path.exists(boost_path):
            with open(boost_path, "r") as boost_test:
                for line in boost_test:
                    if re.match("0", line.rstrip()):
                        boosted = True
        return boosted

    # Audio stuff


class RTSetup:
    # defs for doing things
    def __init__(self):
        self.enabled_path = "/etc/security/limits.d/audio.conf"
        self.disabled_path = "/etc/security/limits.d/audio.conf.disabled"
        self.backup_file = "/usr/share/studio-controls/audio.conf"

    def set_governor(self, enable):
        if enable == True:
            gov = "performance"
        else:
            if os.path.exists("/sys/devices/system/cpu/intel_pstate"):
                gov = "powersave"
            else:
                gov = "ondemand"
        subprocess.run(["/usr/bin/pkexec", "/usr/sbin/studio-system", gov], shell=False)

    def set_boost(self, enable):
        boost_path = "/sys/devices/system/cpu/intel_pstate/no_turbo"
        if os.path.exists(boost_path):
            if enable == True:
                subprocess.run(["/usr/bin/pkexec", "/usr/sbin/studio-system", "boost"], shell=False)
            else:
                subprocess.run(["/usr/bin/pkexec", "/usr/sbin/studio-system", "noboost"], shell=False)


class DevDB:
    ''' class for adding system devices to config data base:
    '''
    #def __init__(self):



    def scan_dev(self, conf):
        ''' scan devices and create device db, scan config and add data'''
        ndevs = 0
        # first reset device number to -1
        for dev in conf['devices']:
            conf['devices'][dev]['number'] = -1

        # now find the real number
        if os.path.exists("/proc/asound/cards"):
            with open("/proc/asound/cards", "r") as cards_file:
                for line in cards_file:
                    # need to find lines with:space/int/space[
                    # ndevs = int from above
                    # last one is highest dev number
                    sub = line.rstrip()[1:]
                    sub2 = sub.split(" ")
                    first_el = line.rstrip()[1:].split(" ")[0]
                    if first_el.isdigit():
                        #if sub2[0].isdigit():
                        #ndevs = int(sub2[0])
                        ndevs = int(first_el)
        ndevs += 1
        for x in range(0, ndevs):
            # card loop
            if os.path.exists(f"/proc/asound/card{str(x)}"):
                if os.path.isfile(f"/proc/asound/card{str(x)}/id"):
                    with open(f"/proc/asound/card{str(x)}/id", "r") as card_file:
                        for line in card_file:
                            # only need one line
                            cname = line.rstrip()
                else:
                    cname = str(x)
            #print(f"cname: {cname} card: {str(x)}")
            if not cname in conf['devices']:
                conf['devices'][cname] = {'number': x, 'usb': False, "internal": False, 'hdmi': False,
                'rates': ['32000', '44100', '48000', '88200', '96000', '192000'],
                'sub': {}}
            else:
                conf['devices'][cname]['number'] = x

            #print(f"{cname}")
            self.get_dev_info(conf['devices'][cname], conf['jack'])


    def  get_dev_info(self, config, jack):
        ''' get device info from system files '''
        cname = ""
        l_usb = False
        l_internal = False
        l_hdmi = False
        sub = 0
        rates = ['32000', '44100', '48000', '88200', '96000', '192000']
        x = int(config['number'])
        #print(f"card: {str(x)}")
        if os.path.exists(f"/proc/asound/card{str(x)}/usbbus"):
            l_usb = True
            # can get supported rates from /proc/asound/card{str(x)}/stream0
            if os.path.exists(f"/proc/asound/card{str(x)}/stream0"):
                with open(f"/proc/asound/card{str(x)}/stream0", "r") as card_file:
                    rates = []
                    for line in card_file:
                        if 'Rates:' in line:
                            fnd_rates = line.split()[1:]
                            for rate in fnd_rates:
                                rate = rate.split(',')[0]
                                if not rate in rates:
                                    rates.append(rate)
        if os.path.exists(f"/proc/asound/card{str(x)}/codec#0"):
            l_internal = True
            # can get supported rates from file above
            with open(f"/proc/asound/card{str(x)}/codec#0", "r") as card_file:
                node = False
                rates = []
                for line in card_file:
                    if 'Node' in line:
                        node = True
                        if 'Digital' in line:
                            node = False
                    if node and 'rates' in line:
                        fnd_rates = line.split()[2:]
                        for rate in fnd_rates:
                            if not rate in rates:
                                rates.append(rate)
        '''if cname == "HDMI" or cname == "NVidia":
            l_hdmi = True'''
        config['hdmi'] = bool(cname in ['HDMI', 'NVidia'])

        config['usb'] = l_usb
        config['internal'] = l_internal
        config['rates'] = rates

        for y in range(0, 20):
            cap = False
            #cap_pid = 0
            play = False
            #play_pid = 0
            if os.path.exists(f"/proc/asound/card{str(x)}/pcm{str(y)}p"):
                play = True

            if os.path.exists(f"/proc/asound/card{str(x)}/pcm{str(y)}c"):
                cap = True

            if play or cap:
                #print(f"sub num: {str(y)} play: {str(play)} cap: {str(cap)}")
                if not str(y) in config['sub']:
                    config['sub'][str(y)] = {'playback': play, 'capture': cap,
                    'play-chan': 0, 'cap-chan': 0,
                    'rate': int(jack['rate']), 'frame': int(jack['frame']),
                    'nperiods': int(jack['period']), 'hide': False}
                else:
                    sub_db = config['sub'][str(y)]
                    sub_db['playback'] = play
                    sub_db['capture'] = cap

        #print(f"Device list: {str(config)}")
        #print(json.dumps(config, indent = 4))



class StudioControls:
    global lock_file
    config_path = "~/.config/autojack/"
    old_config_file = f"{config_path}autojackrc"
    config_file = f"{config_path}autojack.json"


    def __init__(self):
        '''Activate the SysInfo class'''
        # this is a long chunk of code that initializes every thing
        # it should probably be split into tabs at least
        global lock_file
        self.sysinfo = SysInfo()
        c_dir = expanduser(self.config_path)
        if not os.path.isdir(c_dir):
            os.makedirs(c_dir)
        lock_file = expanduser(f"{self.config_path}/studio-controls.lock")
        new_pid = str(os.getpid())
        if os.path.isfile(lock_file):
            with open(lock_file, "r") as lk_file:
                for line in lk_file:
                    # only need one line
                    old_pid = line.rstrip()
            if new_pid != old_pid:
                try:
                    os.kill(int(old_pid), 9)
                except:
                    print("")
                time.sleep(1)
        with open(lock_file, "w") as lk_file:
            lk_file.write(new_pid)
        self.version = ""
        vfile = "/usr/share/studio-controls/version"
        if os.path.isfile(vfile):
            with open(vfile, "r") as version_file:
                for line in version_file:
                    self.version = line.strip()

        signal.signal(signal.SIGHUP, self.sig_handler)
        signal.signal(signal.SIGINT, self.sig_handler)
        signal.signal(signal.SIGQUIT, self.sig_handler)
        signal.signal(signal.SIGILL, self.sig_handler)
        signal.signal(signal.SIGTRAP, self.sig_handler)
        signal.signal(signal.SIGABRT, self.sig_handler)
        signal.signal(signal.SIGBUS, self.sig_handler)
        signal.signal(signal.SIGFPE, self.sig_handler)
        #signal.signal(signal.SIGKILL, self.sig_handler)
        signal.signal(signal.SIGUSR1, self.sig_handler)
        signal.signal(signal.SIGSEGV, self.sig_handler)
        signal.signal(signal.SIGUSR2, self.sig_handler)
        signal.signal(signal.SIGPIPE, self.sig_handler)
        signal.signal(signal.SIGALRM, self.sig_handler)
        signal.signal(signal.SIGTERM, self.sig_handler)



        '''Create the GUI'''
        builder = Gtk.Builder()
        builder.add_from_file("/usr/share/studio-controls/studio-controls.glade")
        '''Get windows'''
        self.window_main = builder.get_object('window_main')
        self.window_help = builder.get_object('window_help')
        self.message_dialog_changes_info = builder.get_object('message_dialog_changes_info')
        self.message_dialog_rt_info = builder.get_object('message_dialog_rt_info')
        self.message_dialog_changes_info.set_transient_for(self.window_main)
        self.message_dialog_rt_info.set_transient_for(self.window_main)
        self.title_label = builder.get_object('label_main_top')
        self.button_msg_ok = builder.get_object('button_msg_ok')
        self.title_label.set_text(f"Studio Set Up Utility (version: {self.version})")
        '''Get buttons for system tab'''
        self.rt_button = builder.get_object('rt_button')
        self.rt_warning = builder.get_object('rt_warning')
        self.combo_governor = builder.get_object('combo_governor')
        self.combo_boost = builder.get_object('combo_boost')
        self.logging_comb = builder.get_object('logging_comb')
        self.combo_fw = builder.get_object('combo_fw')
        '''audio tab stuff'''
        '''master tab'''
        self.jack_device_combo = builder.get_object('jack_device_combo')
        self.jack_usb_dev_combo = builder.get_object('jack_usb_dev_combo')
        self.chan_in_spin = builder.get_object('chan_in_spin')
        self.chan_out_spin = builder.get_object('chan_out_spin')
        self.jack_rate_combo = builder.get_object('jack_rate_combo')
        self.combobox_late = builder.get_object('combobox_late')
        self.combo_periods = builder.get_object('combo_periods')
        self.combo_backend = builder.get_object('combo_backend')
        self.monitor_combo = builder.get_object('monitor_combo')
        self.jack_midi_check = builder.get_object('jack_midi_check')
        self.jack_ind = builder.get_object('jack_ind')
        self.jack_state = builder.get_object('jack_state')
        self.dsp_label = builder.get_object('dsp_label')
        self.xrun_lab = builder.get_object('xrun_lab')

        '''extra tab'''
        self.usb_plug_check = builder.get_object('usb_plug_check')
        self.usb_single_ck = builder.get_object('usb_single_ck')
        self.xdev_select = builder.get_object('xdev_select')
        self.cap_chan_spin = builder.get_object('cap_chan_spin')
        self.play_chan_spin = builder.get_object('play_chan_spin')
        self.hide_check = builder.get_object('hide_check')
        self.xdev_rate_drop = builder.get_object('xdev_rate_drop')
        self.xdev_buff_drop = builder.get_object('xdev_buff_drop')
        self.xdev_nperiods_drop = builder.get_object('xdev_nperiods_drop')
        ''' phones '''
        self.hp_action = builder.get_object('hp_action')
        self.hp_device = builder.get_object('hp_device')
        self.hp_switch = builder.get_object('hp_switch')

        '''pulse tab'''
        self.pj_in_combo = builder.get_object('pj_in_combo')
        self.pj_out_combo = builder.get_object('pj_out_combo')
        self.pj_in_name = builder.get_object('pj_in_name')
        self.pj_out_name = builder.get_object('pj_out_name')
        self.pj_in_con = builder.get_object('pj_in_con')
        self.pj_out_con = builder.get_object('pj_out_con')
        self.pj_in_count = builder.get_object('pj_in_count')
        self.pj_out_count = builder.get_object('pj_out_count')

        '''Session Manager'''
        self.jk_connect_mode = builder.get_object('jk_connect_mode')

        '''Dbus monitoring'''
        user_bus = dbus.SessionBus()
        user_bus.add_signal_receiver(self.db_ses_cb, dbus_interface='org.studio.control.event',
                                     signal_name='V3_5_signal')
        user_bus.add_signal_receiver(self.db_new_usb, dbus_interface='org.studio.control.event',
                                     signal_name='usb_signal')

        # Set default window icon for window managers
        self.window_main.set_default_icon_name('studio-controls')

        '''Check if audio.conf and/or audio.conf.disabled exists, returns are true or false'''
        #self.rt_file = False
        self.jack_file_exists = self.sysinfo.check_pam_files()
        if self.jack_file_exists and self.sysinfo.user_audio():
            rtprio, memlock = self.sysinfo.check_rlimits()
            if rtprio == 0:
                self.rt_button.set_label("Reboot required")
                self.rt_button.set_sensitive(False)
                self.message_dialog_rt_info.show()
                self.rt_warning.set_text("Session restart required for Real Time Permissions")
            else:
                # turn off warning text, check on, deactivate
                self.rt_warning.set_text("")
                self.rt_button.set_label("Real Time Permissions Enabled")
                self.rt_button.set_sensitive(False)

        # show current CPU Governor
        self.combo_governor.append_text("Performance")
        if os.path.exists("/sys/devices/system/cpu/intel_pstate/"):
            self.combo_governor.append_text("Powersave")
        else:
            self.combo_governor.append_text("Ondemand")
        self.in_performance = self.sysinfo.get_performance()
        if self.in_performance:
            self.combo_governor.set_active(0)
        else:
            self.combo_governor.set_active(1)

        # show boost state
        if os.path.exists("/sys/devices/system/cpu/intel_pstate/no_turbo"):
            self.boosted = self.sysinfo.get_boosted()
            if self.boosted:
                self.combo_boost.set_active(1)
            else:
                self.combo_boost.set_active(0)
        else:
            self.combo_boost.set_sensitive(False)

        if os.path.exists("/etc/modprobe.d/blacklist-studio.conf"):
            self.combo_fw.set_active_id("ffado")
            self.combo_backend.append("firewire", "firewire")

        ''' combo_fw and combo_backend '''

        # Audio stuff

        global autojack
        global newusb
        global jack_alive
        global jack_died
        global xrun_count
        global jack_ports_changed
        global jack_time
        jack_time = 3
        xrun_count = 0
        autojack = True
        newusb = False
        jack_alive = False
        jack_died = False
        jack_ports_changed = True
        self.jack_error_mesg = ""
        self.jack_info_mesg = ""
        self.dirty = False
        jack.set_error_function(callback=self.jack_error)
        jack.set_info_function(callback=self.jack_info)

        # read in autojack config file
        c_file = expanduser(self.config_file)
        if not os.path.isfile(c_file):
            # no config file, make one
            cp = subprocess.run(["/usr/bin/convert-studio-controls"],
            universal_newlines=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=False)
        # config file exists, read it in
        print(f"Config file: {c_file}")
        with open(c_file) as f:
            self.conf_db = json.load(f)

        self.devdb = DevDB()
        self.devdb.scan_dev(self.conf_db)
        self.jackdb = self.conf_db['jack']
        self.extra = self.conf_db['extra']

        # Find out if jack is running
        try:
            client = jack.Client('controls', use_exact_name=False, no_start_server=True)
            self.jackdb['on'] = True
            client.close()
        except jack.JackError:
            self.jackdb['on'] = False

        self.logging_comb.set_active_id(str(self.conf_db['log-level']))
        # fill in Jack master widgets
        self.jack_rate_combo.set_active_id(str(self.jackdb['rate']))
        self.combobox_late.set_active_id(str(self.jackdb['frame']))
        self.combo_periods.set_active_id(str(self.jackdb['period']))
        self.combo_backend.set_active_id(self.jackdb['driver'])
        self.chan_in_spin.set_range(1, 128)
        self.chan_out_spin.set_range(1, 128)
        self.chan_in_spin.set_value(self.jackdb['chan-in'])
        self.chan_out_spin.set_value(self.jackdb['chan-out'])
        self.jack_midi_check.set_active(self.extra['a2j'])
        # Fill Extra devices widgets
        self.usb_plug_check.set_active(self.extra['usbauto'])
        if self.extra['usbauto']:
            self.usb_single_ck.set_sensitive(True)
            self.usb_single_ck.set_active(self.extra['usb-single'])
        else:
            self.usb_single_ck.set_sensitive(False)
            self.usb_single_ck.set_active(self.extra['usb-single'])
        #self.hp_action = builder.get_object('hp_action')
        #self.hp_switch = builder.get_object('hp_switch')
        
        # pulse bridge defaults
        self.pj_in_count.set_range(1, 10)
        self.pj_out_count.set_range(1, 10)
        # Session Manger settings
        self.jk_connect_mode.set_active_id(self.jackdb['connect-mode'])

        self.refresh_dropdowns()
        self.pj_in_bridge = ""
        self.pj_out_bridge = ""
        #self.refresh_pulse_io()
        self.refresh_pulse_tab(self.pj_in_bridge, self.pj_out_bridge)

        handlers = {
            "on_window_main_delete_event": self.on_window_main_delete_event,
            "on_window_help_delete_event": self.on_window_help_delete_event,
            "on_main_button_cancel_clicked": self.on_main_button_cancel_clicked,
            "on_main_button_help_clicked": self.on_main_button_help_clicked,
            "combo_governor_changed_cb": self.combo_governor_changed_cb,
            "combo_boost_changed_cb": self.combo_boost_changed_cb,
            "logging_change": self.logging_changed,
            "firewire_cb": self.firewire_cb,
            "rt_button_hit": self.rt_button_hit,
            "on_button_msg_ok_clicked": self.on_button_msg_ok_clicked,
            "on_button_rt_info_ok_clicked": self.on_button_rt_info_ok_clicked,
            "on_button_help_ok_clicked": self.on_button_help_ok_clicked,

            "jack_device_changed": self.jack_device_changed,
            "usb_master_changed": self.usb_master_changed,
            "jack_driver_changed": self.jack_driver_changed,
            "xrun_reset": self.xrun_reset,
            "cb_jack_start": self.cb_jack_start,
            "cb_jack_stop": self.cb_jack_stop,
            "cb_audio_apply": self.cb_audio_apply,
            "mixer_cb": self.mixer_cb,
            "pavucontrol_cb": self.pavucontrol_cb,
            "carla_cb": self.carla_cb,

            "xdev_select_cb": self.xdev_select_cb,
            "xdev_changed": self.xdev_changed,
            "usb_plug_cb": self.usb_plug_cb,
            "hp_action_cb": self.hp_action_cb,
            "hp_switch_cb": self.hp_switch_cb,

            "pj_in_combo_cb": self.pj_in_combo_cb,
            "pj_out_combo_cb": self.pj_out_combo_cb,
            "pj_in_add_cb": self.pj_in_add_cb,
            "pj_out_add_cb": self.pj_out_add_cb,
            "pj_in_rem_cb": self.pj_in_rem_cb,
            "pj_out_rem_cb": self.pj_out_rem_cb,
            "pj_in_name_cb": self.pj_in_name_cb,
            "pj_out_name_cb": self.pj_out_name_cb,

            "ray_cb": self.ray_cb,
            "nsm_cb": self.nsm_cb,
            "agordejo_cb": self.agordejo_cb,
        }
        builder.connect_signals(handlers)

        self.rtsetup = RTSetup()
        self.timeout_id = GLib.timeout_add(500, self.check_jack_status, None)
        self.signal_autojack("ping")
        autojack = False
        print("initialization complete")

    def jack_error(self, mesg):
        if self.jackdb['on']:
            if "not running" in mesg:
                print(f"jack message received: {mesg}")

    def jack_info(self, mesg):
        print(f"jack_info received: {mesg}")

    def db_ses_cb(*args, **kwargs):
        ''' this means we got a responce from autojack, Good'''
        global autojack
        autojack = True
        print("autojack is running")

    def db_new_usb(*args, **kwargs):
        ''' Autojack tells us a USB audio device has been plugged in or
        unplugged. '''
        global newusb
        newusb = True
        print("autojack sees usb change")


    def check_jack_status(self, user_data):
        '''Check if jack has died and the client needs to be
        closed. Check if jack is running then set jack status indicator.
        Check to see if the device lists have changed
        and update the gui if so. Updating GUI prevents race with
        secondary updates caused by updating'''
        # these variables need to be global as they are used by callbacks
        global newusb
        global jack_client
        global jack_alive
        global jack_died
        global xrun_count
        global jack_ports_changed
        global jack_time

        # sent by jackdied() callback
        if jack_died:
            jack_client.deactivate()
            jack_client.close()
            self.refresh_pulse_io()
            self.dirty = True
            jack_died = False
            jack_alive = False

        # device changed, update GUI
        if self.dirty or newusb:
            self.refresh_dropdowns()
            self.dirty = False
            newusb = False

        if jack_ports_changed:
            jack_ports_changed = False
            self.refresh_pulse_io()

        if jack_alive:
            load = jack_client.cpu_load()
            self.dsp_label.set_text(f"DSP: {str(load)[0:4]}%")
            self.xrun_lab.set_text(f" {str(xrun_count)}")
        else:
            if jack_time > 0:
                jack_time = jack_time - 1
                return True
            else:
                jack_time = 4
            self.dsp_label.set_text("DSP: 0%")
            self.xrun_lab.set_text(" 0")
            xrun_count = 0
            try:
                jack_client = jack.Client('controls', use_exact_name=False, no_start_server=True)
            except jack.JackError:
                self.jack_state.set_text(" Stopped")
                self.jack_ind.set_from_icon_name("gtk-stop", 0)
                self.xrun_lab.set_text(" 0")
                xrun_count = 0
                return True
            self.jack_state.set_text(" Running")
            self.jack_ind.set_from_icon_name("gtk-apply", 0)
            # if jack is shutting down we need to know
            jack_client.set_shutdown_callback(self.jackdied)
            # count xruns
            jack_client.set_xrun_callback(self.jackxrun)
            # get notified if a new port is created
            jack_client.set_port_registration_callback(self.new_jack_port)
            jack_client.activate()
            jack_alive = True
            jack_ports_changed = True
            self.refresh_pulse_tab(self.pj_in_bridge, self.pj_out_bridge)
        return True


    def jackdied(state, why, extra):
        '''gets called by jack if it is exiting, we can't clean up
        here... so tell the world jack died with a flag instead'''
        global jack_alive
        global jack_died
        jack_died = True
        jack_alive = False


    def jackxrun(xruntime, extra):
        ''' Got an xrun, count it '''
        global xrun_count
        xrun_count = xrun_count + 1


    def new_jack_port(port_id, new, extra):
        ''' jack has added a port tell someone '''
        global jack_ports_changed
        jack_ports_changed = True


    def xrun_reset(self, button):
        ''' user asked for an xrun reset so do it '''
        global xrun_count
        xrun_count = 0


    def refresh_dropdowns(self):
        '''this call refreshes the device lists for all drop downs
        that use devices. If backend is not "alsa" then the jack master
        and USB master are set to default and no usb master. However,
        all all alsa devices will still be available for bridging and
        as output device.'''
        self.devdb.scan_dev(self.conf_db)
        xdev_current = self.xdev_select.get_active_id()
        self.current_ex_dev = xdev_current

        self.jack_device_combo.set_sensitive(False)
        self.jack_usb_dev_combo.set_sensitive(False)
        self.chan_in_spin.set_sensitive(False)
        self.chan_out_spin.set_sensitive(False)
        self.combo_periods.set_sensitive(False)
        self.jack_rate_combo.set_sensitive(False)
        self.xdev_select.set_sensitive(False)

        self.jack_device_combo.get_model().clear()
        self.xdev_select.get_model().clear()
        self.jack_usb_dev_combo.get_model().clear()
        self.jack_rate_combo.get_model().clear()
        rates =[]
        def_rates = ['32000', '44100', '48000', '88200', '96000', '192000']

        self.jack_usb_dev_combo.append("none", "No USB Master")
        if self.jackdb['usbdev'] == "" or self.jackdb['usbdev'] == "none":
            self.jack_usb_dev_combo.set_active_id("none")

        self.hp_device.get_model().clear()
        self.hp_device.append(self.extra['phone-device'], self.extra['phone-device'])
        self.hp_device.set_active_id(self.extra['phone-device'])

        for this_dev in self.conf_db['devices']:
            dev_db = self.conf_db['devices'][this_dev]
            for this_sub in dev_db['sub']:
                sub_db = dev_db['sub'][this_sub]
                d_type = ""
                if sub_db['capture']:
                    d_type = "capture "
                if sub_db['playback']:
                    if d_type == "":
                        d_type = "playback"
                    else:
                        d_type = f"{d_type} and playback"
                next_id = ""
                dname = ""
                next_d = ""
                if d_type != "":
                    # we have an audio device (not MIDI)
                    subpath = f"/proc/asound/{str(this_dev)}/pcm{str(this_sub)}{str(d_type[0])}/sub0/info"
                    if os.path.exists(subpath):
                        with open(subpath, "r") as info_file:
                            for line in info_file:
                                clean_line = line.rstrip()
                                if re.match("^name:", clean_line):
                                    line_list = clean_line.split(": ", 1)
                                    if len(line_list) > 1:
                                        dname = line_list[1]
                next_id = f"{this_dev},{this_sub},0"
                if dev_db['number'] == -1:
                    dname = "unplugged"
                next_d = f"{next_id} {d_type} ({dname})"
                if "Loopback" in [this_dev]:
                    self.xdev_select.append(next_id, next_d)
                else:
                    self.xdev_select.insert(0, next_id, next_d)
                if not sub_db['hide']:
                    # not hidden so we can add it
                    if not "Loopback" in [this_dev]:
                        self.hp_device.append(next_id, next_d)
                        if dev_db['usb']:
                            self.jack_usb_dev_combo.append(next_id, next_d)
                        else:
                            self.jack_device_combo.append(next_id, next_d)
                    if next_id == self.extra['phone-device']:
                        self.hp_device.set_active_id(next_id)
                    if next_id == self.jackdb['dev']:
                        self.jack_device_combo.set_active_id(next_id)
                        if self.jackdb['usbdev'] == "none":
                            #this is jack master get rates
                            rates = dev_db['rates']
                    if next_id == self.jackdb['usbdev']:
                        self.jack_usb_dev_combo.set_active_id(next_id)
                        #this is jack master get rates
                        rates = dev_db['rates']

        if self.jackdb['driver'] != "alsa" or rates == []:
            rates = def_rates

        for rate in rates:
            self.jack_rate_combo.append(rate, rate)
            if str(self.jackdb['rate']) in rates:
                self.jack_rate_combo.set_active_id(str(self.jackdb['rate']))
            else:
                self.jack_rate_combo.set_active(0)

        self.jack_rate_combo.set_sensitive(True)
        if self.jackdb['driver'] == "alsa":
            self.jack_device_combo.set_sensitive(True)
            self.jack_usb_dev_combo.set_sensitive(True)
            self.combo_periods.set_sensitive(True)
        elif self.jackdb['driver'] == "firewire":
            self.combo_periods.set_sensitive(True)
        elif self.jackdb['driver'] == "dummy":
            self.chan_in_spin.set_sensitive(True)
            self.chan_out_spin.set_sensitive(True)

        self.redraw_extra(xdev_current)


    '''Functions for all the gui controls'''

    def on_window_help_delete_event(self, window, event):
        self.window_help.hide_on_delete()
        return True


    def on_main_button_help_clicked(self, button):
        self.window_help.show()


    def rt_button_hit(self, button):
        subprocess.run(["/usr/bin/pkexec", "/usr/sbin/studio-system", "fix"], shell=False)
        self.rt_button.set_label("Logout required")
        self.rt_button.set_sensitive(False)
        self.message_dialog_rt_info.show()
        self.rt_warning.set_text("Session restart required for Real Time Permissions")


    # system tweaks
    def combo_governor_changed_cb(self, button):
        if button.get_active_text() == "Performance":
            self.rtsetup.set_governor(True)
        else:
            self.rtsetup.set_governor(False)


    def combo_boost_changed_cb(self, button):
        if button.get_active_text() == "on":
            self.rtsetup.set_boost(True)
        else:
            self.rtsetup.set_boost(False)
        self.boosted = self.sysinfo.get_boosted()
        if self.boosted:
            self.combo_boost.set_active(1)
        else:
            self.combo_boost.set_active(0)


    def logging_changed(self, widget):
        newval = widget.get_active_id()
        if self.conf_db['log-level'] != newval:
            self.conf_db['log-level'] = newval
            self.cb_audio_apply(widget)


    def firewire_cb(self, widget):
        newval = widget.get_active_id()
        if newval == "alsa" or newval == "ffado":
            subprocess.run(["/usr/bin/pkexec", "/usr/sbin/studio-system", newval], shell=False)


    # Audio setup call backs
    def xdev_select_cb(self, widget):
        a_id = str(widget.get_active_id())
        if a_id != "None" and a_id != self.current_ex_dev:
            self.redraw_extra(a_id)

    def xdev_changed(self, widget):
        #self.xdev_select.set_sensitive(False)
        if not self.xdev_select.get_sensitive():
            return
        self.cap_chan_spin.set_sensitive(False)
        self.play_chan_spin.set_sensitive(False)
        self.hide_check.set_sensitive(False)
        self.xdev_rate_drop.set_sensitive(False)
        self.xdev_buff_drop.set_sensitive(False)
        self.xdev_nperiods_drop.set_sensitive(False)
        this_dev = str(self.xdev_select.get_active_id())
        dev_db = self.conf_db['devices'][this_dev.split(',')[0]]
        sub_db = dev_db['sub'][str(this_dev.split(',')[1])]

        sub_db['cap-chan'] = self.cap_chan_spin.get_value_as_int()
        sub_db['play-chan'] = self.play_chan_spin.get_value_as_int()
        sub_db['hide'] = self.hide_check.get_active()
        sub_db['rate'] = int(self.xdev_rate_drop.get_active_id())
        sub_db['frame'] = int(self.xdev_buff_drop.get_active_id())
        sub_db['nperiods'] = int(self.xdev_nperiods_drop.get_active_id())
        #get_value_as_int
        self.hide_check.set_sensitive(True)
        if not sub_db['hide']:
            self.cap_chan_spin.set_sensitive(True)
            self.play_chan_spin.set_sensitive(True)
            self.xdev_rate_drop.set_sensitive(True)
            self.xdev_buff_drop.set_sensitive(True)
            self.xdev_nperiods_drop.set_sensitive(True)

    def redraw_extra(self, next_device):
        ''' change all widgets to reflect current values of the
        selected device '''
        frames = [16, 32, 64, 128, 256, 512, 1024, 2048, 4096]
        self.xdev_select.set_sensitive(False)
        self.cap_chan_spin.set_sensitive(False)
        self.play_chan_spin.set_sensitive(False)
        self.hide_check.set_sensitive(False)
        self.xdev_rate_drop.set_sensitive(False)
        self.xdev_buff_drop.set_sensitive(False)
        self.xdev_nperiods_drop.set_sensitive(False)
        self.xdev_rate_drop.get_model().clear()
        self.xdev_buff_drop.get_model().clear()

        if next_device == 'none':
            for def_dev in self.conf_db['devices']:
                next_device = f"{def_dev},0,0"
                break
        #print(f"fixed Next device: {next_device}")
        self.xdev_select.set_active_id(next_device)
        dev_db = self.conf_db['devices'][next_device.split(',')[0]]
        sub_db = dev_db['sub'][str(next_device.split(',')[1])]
        if dev_db['number'] == -1:
            # device is unplugged, some info may be missing
            if not 'rates' in dev_db:
                # the device may not handle all these rates but missing some would be bad
                dev_db['rates'] = ["32000", "44100", "48000", "88200", "96000", "192000"]

        self.cap_chan_spin.set_value(sub_db['cap-chan'])
        self.play_chan_spin.set_value(sub_db['play-chan'])
        self.hide_check.set_active(sub_db['hide'])
        for rate in dev_db['rates']:
            self.xdev_rate_drop.append(str(rate), str(rate))
        self.xdev_rate_drop.set_active_id(str(sub_db['rate']))
        for frame in frames:
            if dev_db['hdmi']:
                if frame == 4096:
                    self.xdev_buff_drop.append(str(frame), str(frame))
            elif dev_db['internal']:
                if frame > 64:
                    self.xdev_buff_drop.append(str(frame), str(frame))
            elif dev_db['usb']:
                if frame > 16:
                    self.xdev_buff_drop.append(str(frame), str(frame))
            else:
                self.xdev_buff_drop.append(str(frame), str(frame))
        self.xdev_buff_drop.set_active_id(str(sub_db['frame']))
        self.xdev_nperiods_drop.set_active_id(str(sub_db['nperiods']))

        self.xdev_select.set_sensitive(True)
        self.hide_check.set_sensitive(True)
        if not sub_db['hide']:
            self.cap_chan_spin.set_sensitive(True)
            self.play_chan_spin.set_sensitive(True)
            self.xdev_rate_drop.set_sensitive(True)
            self.xdev_buff_drop.set_sensitive(True)
            self.xdev_nperiods_drop.set_sensitive(True)


    def hp_action_cb(self, widget):
        a_id = str(widget.get_active_id())
        if a_id == "script":
            print("Script is called ~/.config/autojack/phones.sh")
            # this needs to be shown as a dialog

    def hp_switch_cb(self, button):
        self.signal_autojack("phones")

    def jack_device_changed(self, button):
        a_id = str(button.get_active_id())
        a_desc = str(button.get_active_text())
        if a_id != "None":
            self.conf_db['jack']['dev'] = a_id
            self.dev_desc = a_desc
            if not self.dirty:
                self.dirty = True

    def jack_driver_changed(self, button):
        a_driver = str(button.get_active_text())
        self.conf_db['jack']['driver'] = a_driver
        if not self.dirty:
            self.dirty = True

    def usb_master_changed(self, button):
        a_id = str(button.get_active_id())
        if a_id != "None":
            self.conf_db['jack']['usbdev'] = a_id
        if not self.dirty:
            self.dirty = True

    def usb_plug_cb(self, widget):
        a_id = widget.get_active()
        self.usb_single_ck.set_sensitive(a_id)


    ''' Pulse bridge calls '''

    def refresh_pulse_io(self):
        ''' the ports that can be connected to pulse ports varies
        with what jack offers. This refreshes the two drop downs '''
        global jack_client
        global jack_alive
        self.pj_in_con.set_sensitive(False)
        self.pj_out_con.set_sensitive(False)
        self.monitor_combo.set_sensitive(False)

        in_db = self.conf_db['pulse']['inputs']
        in_exists = False
        if in_db != {}:
            in_exists = True
            if self.pj_in_bridge == "":
                for br in in_db:
                   self.pj_in_bridge = br
                   break 
        self.pj_in_con.get_model().clear()
        self.pj_in_con.append("none", "no connection")
        self.pj_in_con.set_active_id('none')
        if in_exists:
            our_con = in_db[self.pj_in_bridge]['connection']
            if our_con != 'none':
                self.pj_in_con.append(our_con, our_con)
                self.pj_in_con.set_active_id(our_con)

        out_db = self.conf_db['pulse']['outputs']
        out_exists = False
        if out_db != {}:
            out_exists = True
            if self.pj_out_bridge == "":
                for br in out_db:
                   self.pj_out_bridge = br
                   break 
        self.pj_out_con.get_model().clear()
        self.pj_out_con.append("none", "no connection")
        self.pj_out_con.append("monitor", "Main Output Ports")
        self.pj_out_con.set_active_id('none')
        if out_exists:
            our_con = out_db[self.pj_out_bridge]['connection']
            if our_con != "monitor" and our_con != 'none':
                self.pj_out_con.append(our_con, our_con)
            self.pj_out_con.set_active_id(our_con)

        self.monitor_combo.get_model().clear()
        monitor = self.conf_db['extra']["monitor"]
        self.monitor_combo.append(monitor, monitor)
        self.monitor_combo.set_active_id(monitor)

        if jack_alive:
            # get capture ports with audio and hardware
            jack_cap = jack_client.get_ports("", is_audio=True, is_output=True, is_physical=True)
            extra = ""
            last_dev = ""
            for jport in jack_cap:
                port = jport.name
                dev = port.split(':', 1)[0]
                paliases = jport.aliases
                for palias in paliases:
                    adev = palias.split(':', 1)[0]
                    if adev == "system":
                        extra = f" <{dev}>"
                        dev = adev
                        port = palias
                self.pj_in_con.append(port, f"{port} {extra}")
                if in_exists and port == in_db[self.pj_in_bridge]['connection']:
                    self.pj_in_con.remove(self.pj_in_con.get_active())
                    self.pj_in_con.set_active_id(port)

            jack_play = jack_client.get_ports("", is_audio=True, is_input=True, is_physical=True)
            extra = ""
            last_dev = ""
            for jport in jack_play:
                port = jport.name
                dev = port.split(':', 1)[0]
                paliases = jport.aliases
                for palias in paliases:
                    adev = palias.split(':', 1)[0]
                    if adev == "system":
                        extra = f" <{dev}>"
                        dev = adev
                        port = palias
                self.pj_out_con.append(port, f"{port} {extra}")
                self.monitor_combo.append(port, f"{port} {extra}")
                if out_exists and port == out_db[self.pj_out_bridge]['connection']:
                    self.pj_out_con.remove(self.pj_out_con.get_active())
                    self.pj_out_con.set_active_id(port)
                if port == monitor:
                    self.monitor_combo.remove(self.monitor_combo.get_active())
                    self.monitor_combo.set_active_id(monitor)

        else:
            self.pj_in_con.append("none", "Ports cannot be displayed unless JACK is running.")
            self.pj_out_con.append("none", "Ports cannot be displayed unless JACK is running.")
            self.monitor_combo.append("none", "Ports cannot be displayed unless JACK is running.")

        self.pj_in_con.set_sensitive(True)
        self.pj_out_con.set_sensitive(True)
        self.monitor_combo.set_sensitive(True)


    def refresh_pulse_tab(self, i_in, i_out):
        ''' Fill in all pulse related widgets '''
        global jack_ports_changed
        in_db = self.conf_db['pulse']['inputs']
        out_db = self.conf_db['pulse']['outputs']

        self.pj_in_combo.set_sensitive(False)
        self.pj_in_name.set_sensitive(False)
        self.pj_in_count.set_sensitive(False)
        self.pj_out_combo.set_sensitive(False)
        self.pj_out_name.set_sensitive(False)
        self.pj_out_count.set_sensitive(False)
        self.pj_in_con.set_sensitive(False)
        self.pj_out_con.set_sensitive(False)
        self.pj_in_combo.get_model().clear()
        self.pj_out_combo.get_model().clear()
        if in_db == {}:
            # no bridges
            self.pj_in_combo.set_active(0)
            self.pj_in_name.set_text("")
            self.pj_in_count.set_value(1)
            self.pj_in_con.set_active_id('none')
        else:
            for bridge in in_db:
                self.pj_in_combo.append(bridge, bridge)
                if not i_in:
                    i_in = bridge
            self.pj_in_combo.set_active_id(i_in)
            self.pj_in_name.set_text(i_in)
            self.pj_in_count.set_value(in_db[i_in]['count'])
            self.pj_in_con.set_active_id(in_db[i_in]['connection'])
        self.pj_in_bridge = i_in

        if out_db == {}:
            # no bridges
            self.pj_out_combo.set_active(0)
            self.pj_out_name.set_text("")
            self.pj_out_count.set_value(1)
            self.pj_out_con.set_active_id('none')
        else:
            for bridge in out_db:
                self.pj_out_combo.append(bridge, bridge)
                if not i_out:
                    i_out = bridge
            self.pj_out_combo.set_active_id(i_out)
            self.pj_out_name.set_text(i_out)
            self.pj_out_count.set_value(out_db[i_out]['count'])
            self.pj_out_con.set_active_id(out_db[i_out]['connection'])
        self.pj_out_bridge = i_out

        self.pj_in_combo.set_sensitive(True)
        self.pj_in_name.set_sensitive(True)
        self.pj_in_count.set_sensitive(True)
        self.pj_out_combo.set_sensitive(True)
        self.pj_out_name.set_sensitive(True)
        self.pj_out_count.set_sensitive(True)
        self.pj_in_con.set_sensitive(True)
        self.pj_out_con.set_sensitive(True)
        # rebuild the the connection dropdowns
        jack_ports_changed = True


    def pj_in_name_cb(self, widget):
        ''' call back for any pulse bridge input name or connect change
        to current values '''
        if not widget.get_sensitive():
            return
        temp_db =self.conf_db['pulse']['inputs']
        if temp_db != {}:
            old_name = self.pj_in_combo.get_active_id()
            new_name = self.pj_in_name.get_text().split()[0]
            if new_name != old_name:
                if old_name in temp_db:
                    del temp_db[old_name]
                    # because the bridge name is used as a key
                    # it has to be removed and a new one created
                temp_db[new_name] = {
                            'connection': f"{self.pj_in_con.get_active_id()}",
                            'count': int(self.pj_in_count.get_value_as_int())
                            }
            else:
                temp_db[new_name]['connection'] = f"{self.pj_in_con.get_active_id()}"
                temp_db[new_name]['count'] = self.pj_in_count.get_value_as_int()

            self.refresh_pulse_tab(new_name, self.pj_out_bridge)


    def pj_out_name_cb(self, widget):
        ''' call back for any pulse bridge output name or connect change
        to current values '''
        if not widget.get_sensitive():
            return
        temp_db =self.conf_db['pulse']['outputs']
        if temp_db != {}:
            old_name = self.pj_out_combo.get_active_id()
            new_name = self.pj_out_name.get_text().split()[0]
            if new_name != old_name:
                if old_name in temp_db:
                    # because the bridge name is used as a key
                    # it has to be removed and a new one created
                    del temp_db[old_name]
                temp_db[new_name] = {
                            'connection': f"{self.pj_out_con.get_active_id()}",
                            'count': int(self.pj_out_count.get_value_as_int())
                            }
            else:
                temp_db[new_name]['connection'] = f"{self.pj_out_con.get_active_id()}"
                temp_db[new_name]['count'] = self.pj_out_count.get_value_as_int()

            self.refresh_pulse_tab(self.pj_in_bridge, new_name)


    def pj_in_combo_cb(self, widget):
        ''' callback to look at different pa bridge.
        need to save name and connection, then
        refresh name and connections to match '''
        if not widget.get_sensitive():
            return
        if widget.get_active() < 0:
            return
        self.pj_in_bridge = self.pj_in_combo.get_active_id()
        self.refresh_pulse_tab(self.pj_in_bridge, self.pj_out_bridge)


    def pj_out_combo_cb(self, widget):
        ''' callback to look at different pa bridge.
        need to save name and connection, then
        refresh name and connections to match '''
        if not widget.get_sensitive():
            return
        if widget.get_active() < 0:
            return
        self.pj_out_bridge = self.pj_out_combo.get_active_id()
        self.refresh_pulse_tab(self.pj_in_bridge, self.pj_out_bridge)


    def pj_in_add_cb(self, widget):
        ''' need to create a name for the bridge and
        assign connect as "none". Before switching
        to display the new bridge we need to save the
        current bridge info '''
        temp_db =self.conf_db['pulse']['inputs']
        indx = 1
        done = False
        new_name = ""
        while not done:
            new_name = f"pulse_in-{str(indx)}"
            if not new_name in temp_db:
                done = True
            else:
                indx = indx + 1
        temp_db[new_name] = {
                            'connection': "none",
                            'count': 2
                            }
        self.refresh_pulse_tab(new_name, self.pj_out_bridge)


    def pj_out_add_cb(self, widget):
        ''' need to create a name for the bridge and
        assign connect as "none". Before switching
        to display the new bridge we need to save the
        current bridge info '''
        temp_db =self.conf_db['pulse']['outputs']
        indx = 1
        done = False
        new_name = ""
        while not done:
            new_name = f"pulse_out-{str(indx)}"
            if not new_name in temp_db:
                done = True
            else:
                indx = indx + 1
        temp_db[new_name] = {
                            'connection': "none",
                            'count': 2
                            }
        self.refresh_pulse_tab(self.pj_in_bridge, new_name)


    def pj_in_rem_cb(self, widget):
        ''' get index of current bridge
        remove name from list by index
        remove connection from list by index '''
        name = self.pj_in_combo.get_active_id()
        if name in self.conf_db['pulse']['inputs']:
            del self.conf_db['pulse']['inputs'][name]
        self.pj_in_bridge = ""
        for bridge in self.conf_db['pulse']['inputs']:
            self.pj_in_bridge = bridge
            break
        self.refresh_pulse_tab(self.pj_in_bridge, self.pj_out_bridge)


    def pj_out_rem_cb(self, widget):
        ''' get index of current bridge
        remove name from list by index
        remove connection from list by index '''
        name = self.pj_out_combo.get_active_id()
        if name in self.conf_db['pulse']['outputs']:
            del self.conf_db['pulse']['outputs'][name]
        self.pj_in_bridge = ""
        for bridge in self.conf_db['pulse']['outputs']:
            self.pj_out_bridge = bridge
            break
        self.refresh_pulse_tab(self.pj_in_bridge, self.pj_out_bridge)


    ''' External applications calls '''

    def mixer_cb(self, button):
        '''callback for mixer button. This starts QASMixer
        with the device set to whatever is jack master'''
        if self.conf_db['jack']['usbdev'] != "":
            # must be hw:device not hw:device,0,0
            mixdevice = self.conf_db['jack']['usbdev'].split(',', 1)[0]
        else:
            mixdevice = self.conf_db['jack']['dev'].split(',', 1)[0]
        subprocess.Popen(["/usr/bin/qasmixer", "-n", f"--device=hw:{str(mixdevice)}"], shell=False).pid

    def pavucontrol_cb(self, button):
        '''callback for pulse control button, opens pavucontrol'''
        subprocess.Popen(["/usr/bin/pavucontrol"], shell=False).pid

    def carla_cb(self, button):
        '''callback for carla button, opens carla'''
        if os.path.isfile("/usr/bin/carla") and os.access("/usr/bin/carla", os.X_OK):
            subprocess.Popen(["/usr/bin/carla"], shell=False).pid
        else:
            button.set_label("Please Install Carla First")
            button.set_sensitive(False)

    def ray_cb(self, button):
        '''callback for raysession button, opens raysession'''
        if os.path.isfile("/usr/bin/raysession") and os.access("/usr/bin/raysession", os.X_OK):
            subprocess.Popen(["/usr/bin/raysession"], shell=False).pid
        else:
            button.set_label("Please Install RaySession First")
            button.set_sensitive(False)

    def nsm_cb(self, button):
        '''callback for nsm button, opens New Session Manager'''
        if os.path.isfile("/usr/bin/nsm-legacy-gui") and os.access("/usr/bin/nsm-legacy-gui", os.X_OK):
            subprocess.Popen(["/usr/bin/nsm-legacy-gui"], shell=False).pid
        else:
            button.set_label("Please Install New Session Manager First")
            button.set_sensitive(False)

    def agordejo_cb(self, button):
        '''callback for agordejo button, opens agordejo'''
        if os.path.isfile("/usr/bin/agordejo") and os.access("/usr/bin/agordejo", os.X_OK):
            subprocess.Popen(["/usr/bin/agordejo"], shell=False).pid
        else:
            button.set_label("Please Install Agordejo First")
            button.set_sensitive(False)

    ''' Autojack signalling calls '''

    def cb_jack_start(self, button):
        ''' call back for Jack (re)start button'''
        global autojack
        self.jackdb['on'] = True
        self.config_save()
        self.signal_autojack("start")

    def cb_jack_stop(self, button):
        global autojack
        self.jackdb['on'] = False
        self.config_save()
        self.signal_autojack("stop")

    def cb_audio_apply(self, button):
        '''callback for audio tab apply button'''
        global autojack
        self.config_save()
        self.signal_autojack("config")

    def config_save(self):
        ''' Write audio setting to ~/.config/autojack/autojackrc'''
        
        self.jackdb['chan-in'] = int(self.chan_in_spin.get_value())
        self.jackdb['chan-out'] = int(self.chan_out_spin.get_value())
        self.jackdb['rate'] = int(self.jack_rate_combo.get_active_id())
        self.jackdb['frame'] = int(self.combobox_late.get_active_id())
        self.jackdb['period'] = int(self.combo_periods.get_active_id())
        self.jackdb['connect-mode'] = str(self.jk_connect_mode.get_active_id())

        self.extra['a2j'] = self.jack_midi_check.get_active()
        self.extra['usbauto'] = self.usb_plug_check.get_active()
        self.extra['usb-single'] = self.usb_single_ck.get_active()
        self.extra['monitor'] = str(self.monitor_combo.get_active_id())
        self.extra['phone-action'] = str(self.hp_action.get_active_id())
        self.extra['phone-device'] = str(self.hp_device.get_active_id())

        c_file = expanduser(self.config_file)
        if not os.path.isfile(c_file):
            # either first run or old version
            c_dir = expanduser(self.config_path)
            if not os.path.isdir(c_dir):
                os.makedirs(c_dir)
            if os.path.isfile(expanduser(self.old_config_file)):
                os.remove(expanduser(self.old_config_file))
        with open(c_file, 'w') as json_file:
            json.dump(self.conf_db, json_file, indent = 4)
            json_file.write("\n")
            return


    def signal_autojack(self, signal):
        global autojack
        cmd = f"/usr/bin/dbus-send --type=signal / org.studio.control.event.{signal}_signal"
        if autojack:
            subprocess.run(shlex.split(cmd), shell=False)
        else:
            time.sleep(5)
            if autojack:
                subprocess.run(shlex.split(cmd), shell=False)
            else:
                print("Starting Autojack...")
                # first tell any old autojack to die
                subprocess.run(["/usr/bin/dbus-send", "--type=signal", "/", "org.studio.control.event.quit_signal"],
                               shell=False)
                # do it
                subprocess.Popen(["/usr/bin/autojack"], stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL,
                                 stderr=subprocess.STDOUT, shell=False).pid


    def on_button_help_ok_clicked(self, button):
        self.window_help.hide()

    def on_button_rt_info_ok_clicked(self, button):
        self.message_dialog_rt_info.hide()

    ''' All the ways we can die '''
    def on_button_msg_ok_clicked(self, button):
        self.we_die()

    def on_main_button_cancel_clicked(self, button):
        self.we_die()

    def on_window_main_delete_event(self, *args):
        self.we_die()

    def sig_handler(self, signum, frame):
        ''' a handler for system signals that may be sent by the system.
        we want to trap sigint, sigkill and sigterm and do the same as
        above. '''
        self.we_die()

    def we_die(self):
        global jack_alive
        global jack_client
        global lock_file
        if jack_alive:
            jack_client.close()
        if os.path.isfile(lock_file):
            new_pid = str(os.getpid())
            if os.path.isfile(lock_file):
                with open(lock_file, "r") as lk_file:
                    for line in lk_file:
                        # only need one line
                        old_pid = line.rstrip()
                if new_pid == old_pid:
                    os.remove(lock_file)
        Gtk.main_quit()

us = StudioControls()
us.window_main.show_all()

Gtk.main()
