Przeglądaj źródła

Sync from master v4.0.2 .

armink 6 lat temu
rodzic
commit
a383385a3c

+ 3 - 5
components/dfs/src/dfs_file.c

@@ -369,8 +369,7 @@ int dfs_file_stat(const char *path, struct stat *buf)
 
     if ((fs = dfs_filesystem_lookup(fullpath)) == NULL)
     {
-        LOG_E(
-                "can't find mounted filesystem on this path:%s", fullpath);
+        LOG_E("can't find mounted filesystem on this path:%s", fullpath);
         rt_free(fullpath);
 
         return -ENOENT;
@@ -399,8 +398,7 @@ int dfs_file_stat(const char *path, struct stat *buf)
         if (fs->ops->stat == NULL)
         {
             rt_free(fullpath);
-            LOG_E(
-                    "the filesystem didn't implement this function");
+            LOG_E("the filesystem didn't implement this function");
 
             return -ENOSYS;
         }
@@ -565,7 +563,7 @@ void ls(const char *pathname)
                     }
                     else
                     {
-                        rt_kprintf("%-25lu\n", stat.st_size);
+                        rt_kprintf("%-25lu\n", (unsigned long)stat.st_size);
                     }
                 }
                 else

+ 7 - 5
components/drivers/spi/spi_flash_sfud.c

@@ -156,11 +156,11 @@ static sfud_err spi_write_read(const sfud_spi *spi, const uint8_t *write_buf, si
     if(rtt_dev->rt_spi_device->bus->mode & RT_SPI_BUS_MODE_QSPI) {
         qspi_dev = (struct rt_qspi_device *) (rtt_dev->rt_spi_device);
         if (write_size && read_size) {
-            if (rt_qspi_send_then_recv(qspi_dev, write_buf, write_size, read_buf, read_size) == 0) {
+            if (rt_qspi_send_then_recv(qspi_dev, write_buf, write_size, read_buf, read_size) <= 0) {
                 result = SFUD_ERR_TIMEOUT;
             }
         } else if (write_size) {
-            if (rt_qspi_send(qspi_dev, write_buf, write_size) == 0) {
+            if (rt_qspi_send(qspi_dev, write_buf, write_size) <= 0) {
                 result = SFUD_ERR_TIMEOUT;
             }
         }
@@ -173,11 +173,11 @@ static sfud_err spi_write_read(const sfud_spi *spi, const uint8_t *write_buf, si
                 result = SFUD_ERR_TIMEOUT;
             }
         } else if (write_size) {
-            if (rt_spi_send(rtt_dev->rt_spi_device, write_buf, write_size) == 0) {
+            if (rt_spi_send(rtt_dev->rt_spi_device, write_buf, write_size) <= 0) {
                 result = SFUD_ERR_TIMEOUT;
             }
         } else {
-            if (rt_spi_recv(rtt_dev->rt_spi_device, read_buf, read_size) == 0) {
+            if (rt_spi_recv(rtt_dev->rt_spi_device, read_buf, read_size) <= 0) {
                 result = SFUD_ERR_TIMEOUT;
             }
         }
@@ -322,7 +322,7 @@ sfud_err sfud_spi_port_init(sfud_flash *flash) {
 }
 
 #ifdef RT_USING_DEVICE_OPS
-const static struct rt_device_ops flash_device_ops = 
+const static struct rt_device_ops flash_device_ops =
 {
     RT_NULL,
     RT_NULL,
@@ -734,6 +734,7 @@ static void sf(uint8_t argc, char **argv) {
                     for (i = 0; i < size; i += write_size) {
                         result = sfud_write(sfud_dev, addr + i, write_size, write_data);
                         if (result != SFUD_SUCCESS) {
+                            rt_kprintf("Writing %s failed, already wr for %lu bytes, write %d each time\n", sfud_dev->name, i, write_size);
                             break;
                         }
                     }
@@ -761,6 +762,7 @@ static void sf(uint8_t argc, char **argv) {
                         }
 
                         if (result != SFUD_SUCCESS) {
+                            rt_kprintf("Read %s failed, already rd for %lu bytes, read %d each time\n", sfud_dev->name, i, read_size);
                             break;
                         }
                     }

+ 10 - 2
components/drivers/src/pipe.c

@@ -19,6 +19,7 @@
 
 static int pipe_fops_open(struct dfs_fd *fd)
 {
+    int rc = 0;
     rt_device_t device;
     rt_pipe_t *pipe;
 
@@ -31,6 +32,11 @@ static int pipe_fops_open(struct dfs_fd *fd)
     if (device->ref_count == 0)
     {
         pipe->fifo = rt_ringbuffer_create(pipe->bufsz);
+        if (pipe->fifo == RT_NULL)
+        {
+            rc = -RT_ENOMEM;
+            goto __exit;
+        }
     }
 
     switch (fd->flags & O_ACCMODE)
@@ -48,9 +54,10 @@ static int pipe_fops_open(struct dfs_fd *fd)
     }
     device->ref_count ++;
 
+__exit:
     rt_mutex_release(&(pipe->lock));
 
-    return 0;
+    return rc;
 }
 
 static int pipe_fops_close(struct dfs_fd *fd)
@@ -90,7 +97,8 @@ static int pipe_fops_close(struct dfs_fd *fd)
 
     if (device->ref_count == 1)
     {
-        rt_ringbuffer_destroy(pipe->fifo);
+        if (pipe->fifo != RT_NULL)
+            rt_ringbuffer_destroy(pipe->fifo);
         pipe->fifo = RT_NULL;
     }
     device->ref_count --;

+ 1 - 1
components/drivers/src/ringblk_buf.c

@@ -95,9 +95,9 @@ void rt_rbb_destroy(rt_rbb_t rbb)
 {
     RT_ASSERT(rbb);
 
-    rt_free(rbb);
     rt_free(rbb->buf);
     rt_free(rbb->blk_set);
+    rt_free(rbb);
 
 }
 RTM_EXPORT(rt_rbb_destroy);

+ 20 - 0
components/libc/compilers/minilibc/signal.h

@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2006-2018, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2017-09-12     Bernard      The first version
+ */
+
+#ifndef SIGNAL_H__
+#define SIGNAL_H__
+
+#include <libc/libc_signal.h>
+
+#define SIG_DFL ((_sig_func_ptr)0)  /* Default action */
+#define SIG_IGN ((_sig_func_ptr)1)  /* Ignore action */
+#define SIG_ERR ((_sig_func_ptr)-1) /* Error return */
+
+#endif

+ 7 - 3
components/utilities/Kconfig

@@ -2,12 +2,16 @@ menu "Utilities"
 
 config RT_USING_RYM
     bool "Enable Ymodem"
-    depends on RT_USING_DFS = y
     default n
 
     if RT_USING_RYM
-        config YMODEM_DISABLE_CRC_TABLE
-        bool "Disable CRC Table"
+        config YMODEM_USING_CRC_TABLE
+        bool "Enable CRC Table in Ymodem"
+        default n
+
+        config YMODEM_USING_FILE_TRANSFER
+        bool "Enable file transfer feature"
+        select RT_USING_DFS
         default n
     endif
 

+ 8 - 1
components/utilities/ymodem/SConscript

@@ -1,8 +1,15 @@
 from building import *
 
 cwd     = GetCurrentDir()
-src     = Glob('*.c')
+src     = Split('''
+ymodem.c
+''')
+ 
 CPPPATH = [cwd]
+
+if GetDepend('RT_USING_DFS') and GetDepend('YMODEM_USING_FILE_TRANSFER'):
+    src += ['ry_sy.c']
+
 group   = DefineGroup('Utilities', src, depend = ['RT_USING_RYM'], CPPPATH = CPPPATH)
 
 Return('group')

+ 2 - 1
components/utilities/ymodem/ry_sy.c

@@ -11,8 +11,9 @@
 #include <rtthread.h>
 #include <ymodem.h>
 #include <dfs_posix.h>
+
+#include <stdio.h>
 #include <stdlib.h>
-#include <board.h>
 #include <string.h>
 
 struct custom_ctx

+ 1 - 1
components/utilities/ymodem/ymodem.c

@@ -13,7 +13,7 @@
 #include <rthw.h>
 #include "ymodem.h"
 
-#ifndef YMODEM_DISABLE_CRC_TABLE
+#ifdef YMODEM_USING_CRC_TABLE 
 static const rt_uint16_t ccitt_table[256] =
 {
     0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7,

+ 5 - 10
include/libc/libc_signal.h

@@ -63,13 +63,14 @@ typedef struct siginfo siginfo_t;
 #define SI_MESGQ    0x05    /* Signal generated by arrival of a 
                                message on an empty message queue. */
 
-#ifdef RT_USING_NEWLIB
-#include <sys/signal.h>
+#if !defined(RT_USING_NEWLIB)
+typedef void (*_sig_func_ptr)(int);
+typedef unsigned long sigset_t;
 #endif
 
-#if defined(__CC_ARM) || defined(__CLANG_ARM)
 #include <signal.h>
-typedef unsigned long sigset_t;
+
+#if defined(__CC_ARM) || defined(__CLANG_ARM)
 
 #define SIGHUP       1
 /* #define SIGINT       2 */
@@ -105,8 +106,6 @@ typedef unsigned long sigset_t;
 #define SIG_BLOCK   1   /* set of signals to block */
 #define SIG_UNBLOCK 2   /* set of signals to, well, unblock */
 
-typedef void (*_sig_func_ptr)(int);
-
 struct sigaction
 {
     _sig_func_ptr sa_handler;
@@ -124,8 +123,6 @@ int sigprocmask (int how, const sigset_t *set, sigset_t *oset);
 int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
 
 #elif defined(__IAR_SYSTEMS_ICC__)
-#include <signal.h>
-typedef unsigned long sigset_t;
 
 #define SIGHUP       1
 #define SIGINT       2
@@ -161,8 +158,6 @@ typedef unsigned long sigset_t;
 #define SIG_BLOCK   1   /* set of signals to block */
 #define SIG_UNBLOCK 2   /* set of signals to, well, unblock */
 
-typedef void (*_sig_func_ptr)(int);
-
 struct sigaction
 {
     _sig_func_ptr sa_handler;

+ 1 - 0
include/rtthread.h

@@ -137,6 +137,7 @@ rt_err_t rt_thread_delete(rt_thread_t thread);
 
 rt_err_t rt_thread_yield(void);
 rt_err_t rt_thread_delay(rt_tick_t tick);
+rt_err_t rt_thread_delay_until(rt_tick_t *tick, rt_tick_t inc_tick);
 rt_err_t rt_thread_mdelay(rt_int32_t ms);
 rt_err_t rt_thread_control(rt_thread_t thread, int cmd, void *arg);
 rt_err_t rt_thread_suspend(rt_thread_t thread);

+ 57 - 0
src/thread.c

@@ -534,6 +534,63 @@ rt_err_t rt_thread_delay(rt_tick_t tick)
 }
 RTM_EXPORT(rt_thread_delay);
 
+/**
+ * This function will let current thread delay until (*tick + inc_tick).
+ *
+ * @param tick the tick of last wakeup.
+ * @param inc_tick the increment tick
+ *
+ * @return RT_EOK
+ */
+rt_err_t rt_thread_delay_until(rt_tick_t *tick, rt_tick_t inc_tick)
+{
+    register rt_base_t level;
+    struct rt_thread *thread;
+
+    RT_ASSERT(tick != RT_NULL);
+
+    /* set to current thread */
+    thread = rt_thread_self();
+    RT_ASSERT(thread != RT_NULL);
+    RT_ASSERT(rt_object_get_type((rt_object_t)thread) == RT_Object_Class_Thread);
+
+    /* disable interrupt */
+    level = rt_hw_interrupt_disable();
+
+    if (rt_tick_get() - *tick < inc_tick)
+    {
+        *tick = rt_tick_get() - *tick + inc_tick;
+
+        /* suspend thread */
+        rt_thread_suspend(thread);
+
+        /* reset the timeout of thread timer and start it */
+        rt_timer_control(&(thread->thread_timer), RT_TIMER_CTRL_SET_TIME, tick);
+        rt_timer_start(&(thread->thread_timer));
+
+        /* enable interrupt */
+        rt_hw_interrupt_enable(level);
+
+        rt_schedule();
+
+        /* clear error number of this thread to RT_EOK */
+        if (thread->error == -RT_ETIMEOUT)
+        {
+            thread->error = RT_EOK;
+        }
+    }
+    else
+    {
+        rt_hw_interrupt_enable(level);
+    }
+
+    /* get the wakeup tick */
+    *tick = rt_tick_get();
+
+    return RT_EOK;
+}
+RTM_EXPORT(rt_thread_delay_until);
+
 /**
  * This function will let current thread delay for some milliseconds.
  *

+ 0 - 1206
tools/pymenuconfig.py

@@ -1,1206 +0,0 @@
-# SPDX-License-Identifier: ISC
-# -*- coding: utf-8 -*-
-
-"""
-Overview
-========
-
-pymenuconfig is a small and simple frontend to Kconfiglib that's written
-entirely in Python using Tkinter as its GUI toolkit.
-
-Motivation
-==========
-
-Kconfig is a nice and powerful framework for build-time configuration and lots
-of projects already benefit from using it. Kconfiglib allows to utilize power of
-Kconfig by using scripts written in pure Python, without requiring one to build
-Linux kernel tools written in C (this can be quite tedious on anything that's
-not *nix). The aim of this project is to implement simple and small Kconfiglib
-GUI frontend that runs on as much systems as possible.
-
-Tkinter GUI toolkit is a natural choice if portability is considered, as it's
-a part of Python standard library and is available virtually in every CPython
-installation.
-
-
-User interface
-==============
-
-I've tried to replicate look and fill of Linux kernel 'menuconfig' tool that
-many users are used to, including keyboard-oriented control and textual
-representation of menus with fixed-width font.
-
-
-Usage
-=====
-
-The pymenuconfig module is executable and parses command-line args, so the
-most simple way to run menuconfig is to execute script directly:
-
-  python pymenuconfig.py --kconfig Kconfig
-
-As with most command-line tools list of options can be obtained with '--help':
-
-  python pymenuconfig.py --help
-
-If installed with setuptools, one can run it like this:
-
-  python -m pymenuconfig --kconfig Kconfig
-
-In case you're making a wrapper around menuconfig, you can either call main():
-
-  import pymenuconfig
-  pymenuconfig.main(['--kconfig', 'Kconfig'])
-
-Or import MenuConfig class, instantiate it and manually run Tkinter's mainloop:
-
-  import tkinter
-  import kconfiglib
-  from pymenuconfig import MenuConfig
-
-  kconfig = kconfiglib.Kconfig()
-  mconf = MenuConfig(kconfig)
-  tkinter.mainloop()
-
-"""
-
-from __future__ import print_function
-
-import os
-import sys
-import argparse
-import kconfiglib
-
-# Tk is imported differently depending on python major version
-if sys.version_info[0] < 3:
-    import Tkinter as tk
-    import tkFont as font
-    import tkFileDialog as filedialog
-    import tkMessageBox as messagebox
-else:
-    import tkinter as tk
-    from tkinter import font
-    from tkinter import filedialog
-    from tkinter import messagebox
-
-
-class ListEntry(object):
-    """
-    Represents visible menu node and holds all information related to displaying
-    menu node in a Listbox.
-
-    Instances of this class also handle all interaction with main window.
-    A node is displayed as a single line of text:
-      PREFIX INDENT BODY POSTFIX
-    - The PREFIX is always 3 characters or more and can take following values:
-      '   ' comment, menu, bool choice, etc.
-      Inside menus:
-      '< >' bool symbol has value 'n'
-      '<*>' bool symbol has value 'y'
-      '[ ]' tristate symbol has value 'n'
-      '[M]' tristate symbol has value 'm'
-      '[*]' tristate symbol has value 'y'
-      '- -' symbol has value 'n' that's not editable
-      '-M-' symbol has value 'm' that's not editable
-      '-*-' symbol has value 'y' that's not editable
-      '(M)' tristate choice has value 'm'
-      '(*)' tristate choice has value 'y'
-      '(some value)' value of non-bool/tristate symbols
-      Inside choices:
-      '( )' symbol has value 'n'
-      '(M)' symbol has value 'm'
-      '(*)' symbol has value 'y'
-    - INDENT is a sequence of space characters. It's used in implicit menus, and
-      adds 2 spaces for each nesting level
-    - BODY is a menu node prompt. '***' is added if node is a comment
-    - POSTFIX adds '(NEW)', '--->' and selected choice symbol where applicable
-
-    Attributes:
-
-    node:
-      MenuNode instance this ListEntry is created for.
-
-    visible:
-      Whether entry should be shown in main window.
-
-    text:
-      String to display in a main window's Listbox.
-
-    refresh():
-      Updates .visible and .text attribute values.
-
-    set_tristate_value():
-      Set value for bool/tristate symbols, value should be one of 0,1,2 or None.
-      Usually it's called when user presses 'y', 'n', 'm' key.
-
-    set_str_value():
-      Set value for non-bool/tristate symbols, value is a string. Usually called
-      with a value returned by one of MenuConfig.ask_for_* methods.
-
-    toggle():
-      Toggle bool/tristate symbol value. Called when '<Space>' key is pressed in
-      a main window. Also selects choice value.
-
-    select():
-      Called when '<Return>' key is pressed in a main window with 'SELECT'
-      action selected. Displays submenu, choice selection menu, or just selects
-      choice value. For non-bool/tristate symbols asks MenuConfig window to
-      handle value input via one of MenuConfig.ask_for_* methods.
-
-    show_help():
-      Called when '<Return>' key is pressed in a main window with 'HELP' action
-      selected. Prepares text help and calls MenuConfig.show_text() to display
-      text window.
-    """
-
-    # How to display value of BOOL and TRISTATE symbols
-    TRI_TO_DISPLAY = {
-        0: ' ',
-        1: 'M',
-        2: '*'
-    }
-
-    def __init__(self, mconf, node, indent):
-        self.indent = indent
-        self.node = node
-        self.menuconfig = mconf
-        self.visible = False
-        self.text = None
-
-    def __str__(self):
-        return self.text
-
-    def _is_visible(self):
-        node = self.node
-        v = True
-        v = v and node.prompt is not None
-        # It should be enough to check if prompt expression is not false and
-        # for menu nodes whether 'visible if' is not false
-        v = v and kconfiglib.expr_value(node.prompt[1]) > 0
-        if node.item == kconfiglib.MENU:
-            v = v and kconfiglib.expr_value(node.visibility) > 0
-        # If node references Symbol, then we also account for symbol visibility
-        # TODO: need to re-think whether this is needed
-        if isinstance(node.item, kconfiglib.Symbol):
-            if node.item.type in (kconfiglib.BOOL, kconfiglib.TRISTATE):
-                v = v and len(node.item.assignable) > 0
-            else:
-                v = v and node.item.visibility > 0
-        return v
-
-    def _get_text(self):
-        """
-        Compute textual representation of menu node (a line in ListView)
-        """
-        node = self.node
-        item = node.item
-        # Determine prefix
-        prefix = '   '
-        if (isinstance(item, kconfiglib.Symbol) and item.choice is None or
-            isinstance(item, kconfiglib.Choice) and item.type is kconfiglib.TRISTATE):
-            # The node is for either a symbol outside of choice statement
-            # or a tristate choice
-            if item.type in (kconfiglib.BOOL, kconfiglib.TRISTATE):
-                value = ListEntry.TRI_TO_DISPLAY[item.tri_value]
-                if len(item.assignable) > 1:
-                    # Symbol is editable
-                    if 1 in item.assignable:
-                        prefix = '<{}>'.format(value)
-                    else:
-                        prefix = '[{}]'.format(value)
-                else:
-                    # Symbol is not editable
-                    prefix = '-{}-'.format(value)
-            else:
-                prefix = '({})'.format(item.str_value)
-        elif isinstance(item, kconfiglib.Symbol) and item.choice is not None:
-            # The node is for symbol inside choice statement
-            if item.type in (kconfiglib.BOOL, kconfiglib.TRISTATE):
-                value = ListEntry.TRI_TO_DISPLAY[item.tri_value]
-                if len(item.assignable) > 0:
-                    # Symbol is editable
-                    prefix = '({})'.format(value)
-                else:
-                    # Symbol is not editable
-                    prefix = '-{}-'.format(value)
-            else:
-                prefix = '({})'.format(item.str_value)
-
-        # Prefix should be at least 3 chars long
-        if len(prefix) < 3:
-            prefix += ' ' * (3 - len(prefix))
-        # Body
-        body = ''
-        if node.prompt is not None:
-            if item is kconfiglib.COMMENT:
-                body = '*** {} ***'.format(node.prompt[0])
-            else:
-                body = node.prompt[0]
-        # Suffix
-        is_menu = False
-        is_new = False
-        if (item is kconfiglib.MENU
-            or isinstance(item, kconfiglib.Symbol) and node.is_menuconfig
-            or isinstance(item, kconfiglib.Choice)):
-            is_menu = True
-        if isinstance(item, kconfiglib.Symbol) and item.user_value is None:
-            is_new = True
-        # For symbol inside choice that has 'y' value, '(NEW)' is not displayed
-        if (isinstance(item, kconfiglib.Symbol)
-            and item.choice and item.choice.tri_value == 2):
-            is_new = False
-        # Choice selection - displayed only for choices which have 'y' value
-        choice_selection = None
-        if isinstance(item, kconfiglib.Choice) and node.item.str_value == 'y':
-            choice_selection = ''
-            if item.selection is not None:
-                sym = item.selection
-                if sym.nodes and sym.nodes[0].prompt is not None:
-                    choice_selection = sym.nodes[0].prompt[0]
-        text = '  {prefix} {indent}{body}{choice}{new}{menu}'.format(
-            prefix=prefix,
-            indent='  ' * self.indent,
-            body=body,
-            choice='' if choice_selection is None else ' ({})'.format(
-                choice_selection
-            ),
-            new=' (NEW)' if is_new else '',
-            menu=' --->' if is_menu else ''
-        )
-        return text
-
-    def refresh(self):
-        self.visible = self._is_visible()
-        self.text = self._get_text()
-
-    def set_tristate_value(self, value):
-        """
-        Call to change value of BOOL, TRISTATE symbols
-
-        It's preferred to use this instead of item.set_value as it handles
-        all necessary interaction with MenuConfig window when symbol value
-        changes
-
-        None value is accepted but ignored
-        """
-        item = self.node.item
-        if (isinstance(item, (kconfiglib.Symbol, kconfiglib.Choice))
-            and item.type in (kconfiglib.BOOL, kconfiglib.TRISTATE)
-            and value is not None):
-            if value in item.assignable:
-                item.set_value(value)
-            elif value == 2 and 1 in item.assignable:
-                print(
-                    'Symbol {} value is limited to \'m\'. Setting value \'m\' instead of \'y\''.format(item.name),
-                    file=sys.stderr
-                )
-                item.set_value(1)
-            self.menuconfig.mark_as_changed()
-            self.menuconfig.refresh_display()
-
-    def set_str_value(self, value):
-        """
-        Call to change value of HEX, INT, STRING symbols
-
-        It's preferred to use this instead of item.set_value as it handles
-        all necessary interaction with MenuConfig window when symbol value
-        changes
-
-        None value is accepted but ignored
-        """
-        item = self.node.item
-        if (isinstance(item, kconfiglib.Symbol)
-            and item.type in (kconfiglib.INT, kconfiglib.HEX, kconfiglib.STRING)
-            and value is not None):
-            item.set_value(value)
-            self.menuconfig.mark_as_changed()
-            self.menuconfig.refresh_display()
-
-    def toggle(self):
-        """
-        Called when <space> key is pressed
-        """
-        item = self.node.item
-        if (isinstance(item, (kconfiglib.Symbol, kconfiglib.Choice))
-            and item.type in (kconfiglib.BOOL, kconfiglib.TRISTATE)):
-            value = item.tri_value
-            # Find next value in Symbol/Choice.assignable, or use assignable[0]
-            try:
-                it = iter(item.assignable)
-                while value != next(it):
-                    pass
-                self.set_tristate_value(next(it))
-            except StopIteration:
-                self.set_tristate_value(item.assignable[0])
-
-    def select(self):
-        """
-        Called when <Return> key is pressed and SELECT action is selected
-        """
-        item = self.node.item
-        # - Menu: dive into submenu
-        # - INT, HEX, STRING symbol: raise prompt to enter symbol value
-        # - BOOL, TRISTATE symbol inside 'y'-valued Choice: set 'y' value
-        if (item is kconfiglib.MENU
-            or isinstance(item, kconfiglib.Symbol) and self.node.is_menuconfig
-            or isinstance(item, kconfiglib.Choice)):
-            # Dive into submenu
-            self.menuconfig.show_submenu(self.node)
-        elif (isinstance(item, kconfiglib.Symbol) and item.type in
-              (kconfiglib.INT, kconfiglib.HEX, kconfiglib.STRING)):
-            # Raise prompt to enter symbol value
-            ident = self.node.prompt[0] if self.node.prompt is not None else None
-            title = 'Symbol: {}'.format(item.name)
-            if item.type is kconfiglib.INT:
-                # Find enabled ranges
-                ranges = [
-                    (int(start.str_value), int(end.str_value))
-                    for start, end, expr in item.ranges
-                    if kconfiglib.expr_value(expr) > 0
-                ]
-                # Raise prompt
-                self.set_str_value(str(self.menuconfig.ask_for_int(
-                    ident=ident,
-                    title=title,
-                    value=item.str_value,
-                    ranges=ranges
-                )))
-            elif item.type is kconfiglib.HEX:
-                # Find enabled ranges
-                ranges = [
-                    (int(start.str_value, base=16), int(end.str_value, base=16))
-                    for start, end, expr in item.ranges
-                    if kconfiglib.expr_value(expr) > 0
-                ]
-                # Raise prompt
-                self.set_str_value(hex(self.menuconfig.ask_for_hex(
-                    ident=ident,
-                    title=title,
-                    value=item.str_value,
-                    ranges=ranges
-                )))
-            elif item.type is kconfiglib.STRING:
-                # Raise prompt
-                self.set_str_value(self.menuconfig.ask_for_string(
-                    ident=ident,
-                    title=title,
-                    value=item.str_value
-                ))
-        elif (isinstance(item, kconfiglib.Symbol)
-              and item.choice is not None and item.choice.tri_value == 2):
-            # Symbol inside choice -> set symbol value to 'y'
-            self.set_tristate_value(2)
-
-    def show_help(self):
-        node = self.node
-        item = self.node.item
-        if isinstance(item, (kconfiglib.Symbol, kconfiglib.Choice)):
-            title = 'Help for symbol: {}'.format(item.name)
-            if node.help:
-                help = node.help
-            else:
-                help = 'There is no help available for this option.\n'
-            lines = []
-            lines.append(help)
-            lines.append(
-                'Symbol: {} [={}]'.format(
-                    item.name if item.name else '<UNNAMED>', item.str_value
-                )
-            )
-            lines.append('Type  : {}'.format(kconfiglib.TYPE_TO_STR[item.type]))
-            for n in item.nodes:
-                lines.append('Prompt: {}'.format(n.prompt[0] if n.prompt else '<EMPTY>'))
-                lines.append('  Defined at {}:{}'.format(n.filename, n.linenr))
-                lines.append('  Depends on: {}'.format(kconfiglib.expr_str(n.dep)))
-            text = '\n'.join(lines)
-        else:
-            title = 'Help'
-            text = 'Help not available for this menu node.\n'
-        self.menuconfig.show_text(text, title)
-        self.menuconfig.refresh_display()
-
-
-class EntryDialog(object):
-    """
-    Creates modal dialog (top-level Tk window) with labels, entry box and two
-    buttons: OK and CANCEL.
-    """
-    def __init__(self, master, text, title, ident=None, value=None):
-        self.master = master
-        dlg = self.dlg = tk.Toplevel(master)
-        self.dlg.withdraw() #hiden window
-        dlg.title(title)
-        # Identifier label
-        if ident is not None:
-            self.label_id = tk.Label(dlg, anchor=tk.W, justify=tk.LEFT)
-            self.label_id['font'] = font.nametofont('TkFixedFont')
-            self.label_id['text'] = '# {}'.format(ident)
-            self.label_id.pack(fill=tk.X, padx=2, pady=2)
-        # Label
-        self.label = tk.Label(dlg, anchor=tk.W, justify=tk.LEFT)
-        self.label['font'] = font.nametofont('TkFixedFont')
-        self.label['text'] = text
-        self.label.pack(fill=tk.X, padx=10, pady=4)
-        # Entry box
-        self.entry = tk.Entry(dlg)
-        self.entry['font'] = font.nametofont('TkFixedFont')
-        self.entry.pack(fill=tk.X, padx=2, pady=2)
-        # Frame for buttons
-        self.frame = tk.Frame(dlg)
-        self.frame.pack(padx=2, pady=2)
-        # Button
-        self.btn_accept = tk.Button(self.frame, text='< Ok >', command=self.accept)
-        self.btn_accept['font'] = font.nametofont('TkFixedFont')
-        self.btn_accept.pack(side=tk.LEFT, padx=2)
-        self.btn_cancel = tk.Button(self.frame, text='< Cancel >', command=self.cancel)
-        self.btn_cancel['font'] = font.nametofont('TkFixedFont')
-        self.btn_cancel.pack(side=tk.LEFT, padx=2)
-        # Bind Enter and Esc keys
-        self.dlg.bind('<Return>', self.accept)
-        self.dlg.bind('<Escape>', self.cancel)
-        # Dialog is resizable only by width
-        self.dlg.resizable(1, 0)
-        # Set supplied value (if any)
-        if value is not None:
-            self.entry.insert(0, value)
-            self.entry.selection_range(0, tk.END)
-        # By default returned value is None. To caller this means that entry
-        # process was cancelled
-        self.value = None
-        # Modal dialog
-        dlg.transient(master)
-        dlg.grab_set()
-        # Center dialog window
-        _center_window_above_parent(master, dlg)
-        self.dlg.deiconify() # show window
-        # Focus entry field
-        self.entry.focus_set()
-
-    def accept(self, ev=None):
-        self.value = self.entry.get()
-        self.dlg.destroy()
-
-    def cancel(self, ev=None):
-        self.dlg.destroy()
-
-
-class TextDialog(object):
-    def __init__(self, master, text, title):
-        self.master = master
-        dlg = self.dlg = tk.Toplevel(master)
-        self.dlg.withdraw() #hiden window
-        dlg.title(title)
-        dlg.minsize(600,400)
-        # Text
-        self.text = tk.Text(dlg, height=1)
-        self.text['font'] = font.nametofont('TkFixedFont')
-        self.text.insert(tk.END, text)
-        # Make text read-only
-        self.text['state'] = tk.DISABLED
-        self.text.pack(fill=tk.BOTH, expand=1, padx=4, pady=4)
-        # Frame for buttons
-        self.frame = tk.Frame(dlg)
-        self.frame.pack(padx=2, pady=2)
-        # Button
-        self.btn_accept = tk.Button(self.frame, text='< Ok >', command=self.accept)
-        self.btn_accept['font'] = font.nametofont('TkFixedFont')
-        self.btn_accept.pack(side=tk.LEFT, padx=2)
-        # Bind Enter and Esc keys
-        self.dlg.bind('<Return>', self.accept)
-        self.dlg.bind('<Escape>', self.cancel)
-        # Modal dialog
-        dlg.transient(master)
-        dlg.grab_set()
-        # Center dialog window
-        _center_window_above_parent(master, dlg)
-        self.dlg.deiconify() # show window
-        # Focus entry field
-        self.text.focus_set()
-
-    def accept(self, ev=None):
-        self.dlg.destroy()
-
-    def cancel(self, ev=None):
-        self.dlg.destroy()
-
-
-class MenuConfig(object):
-    (
-        ACTION_SELECT,
-        ACTION_EXIT,
-        ACTION_HELP,
-        ACTION_LOAD,
-        ACTION_SAVE,
-        ACTION_SAVE_AS
-    ) = range(6)
-
-    ACTIONS = (
-        ('Select', ACTION_SELECT),
-        ('Exit', ACTION_EXIT),
-        ('Help', ACTION_HELP),
-        ('Load', ACTION_LOAD),
-        ('Save', ACTION_SAVE),
-        ('Save as', ACTION_SAVE_AS),
-    )
-
-    def __init__(self, kconfig, __silent=None):
-        self.kconfig = kconfig
-        self.__silent = __silent
-        if self.__silent is True:
-            return
-
-        # Instantiate Tk widgets
-        self.root = tk.Tk()
-        self.root.withdraw() #hiden window
-        dlg = self.root
-
-        # Window title
-        dlg.title('pymenuconfig')
-        # Some empirical window size
-        dlg.minsize(500, 300)
-        dlg.geometry('800x600')
-
-        # Label that shows position in menu tree
-        self.label_position = tk.Label(
-            dlg,
-            anchor=tk.W,
-            justify=tk.LEFT,
-            font=font.nametofont('TkFixedFont')
-        )
-        self.label_position.pack(fill=tk.X, padx=2)
-
-        # 'Tip' frame and text
-        self.frame_tip = tk.LabelFrame(
-            dlg,
-            text='Tip'
-        )
-        self.label_tip = tk.Label(
-            self.frame_tip,
-            anchor=tk.W,
-            justify=tk.LEFT,
-            font=font.nametofont('TkFixedFont')
-        )
-        self.label_tip['text'] = '\n'.join([
-            'Arrow keys navigate the menu. <Enter> performs selected operation (set of buttons at the bottom)',
-            'Pressing <Y> includes, <N> excludes, <M> modularizes features',
-            'Press <Esc> to go one level up. Press <Esc> at top level to exit',
-            'Legend: [*] built-in  [ ] excluded  <M> module  < > module capable'
-        ])
-        self.label_tip.pack(fill=tk.BOTH, expand=1, padx=4, pady=4)
-        self.frame_tip.pack(fill=tk.X, padx=2)
-
-        # Main ListBox where all the magic happens
-        self.list = tk.Listbox(
-            dlg,
-            selectmode=tk.SINGLE,
-            activestyle=tk.NONE,
-            font=font.nametofont('TkFixedFont'),
-            height=1,
-        )
-        self.list['foreground'] = 'Blue'
-        self.list['background'] = 'Gray95'
-        # Make selection invisible
-        self.list['selectbackground'] = self.list['background']
-        self.list['selectforeground'] = self.list['foreground']
-        self.list.pack(fill=tk.BOTH, expand=1, padx=20, ipadx=2)
-
-        # Frame with radio buttons
-        self.frame_radio = tk.Frame(dlg)
-        self.radio_buttons = []
-        self.tk_selected_action = tk.IntVar()
-        for text, value in MenuConfig.ACTIONS:
-            btn = tk.Radiobutton(
-                self.frame_radio,
-                variable=self.tk_selected_action,
-                value=value
-            )
-            btn['text'] = '< {} >'.format(text)
-            btn['font'] = font.nametofont('TkFixedFont')
-            btn['indicatoron'] = 0
-            btn.pack(side=tk.LEFT)
-            self.radio_buttons.append(btn)
-        self.frame_radio.pack(anchor=tk.CENTER, pady=4)
-        # Label with status information
-        self.tk_status = tk.StringVar()
-        self.label_status = tk.Label(
-            dlg,
-            textvariable=self.tk_status,
-            anchor=tk.W,
-            justify=tk.LEFT,
-            font=font.nametofont('TkFixedFont')
-        )
-        self.label_status.pack(fill=tk.X, padx=4, pady=4)
-        # Center window
-        _center_window(self.root, dlg)
-        self.root.deiconify() # show window
-        # Disable keyboard focus on all widgets ...
-        self._set_option_to_all_children(dlg, 'takefocus', 0)
-        # ... except for main ListBox
-        self.list['takefocus'] = 1
-        self.list.focus_set()
-        # Bind keys
-        dlg.bind('<Escape>', self.handle_keypress)
-        dlg.bind('<space>', self.handle_keypress)
-        dlg.bind('<Return>', self.handle_keypress)
-        dlg.bind('<Right>', self.handle_keypress)
-        dlg.bind('<Left>', self.handle_keypress)
-        dlg.bind('<Up>', self.handle_keypress)
-        dlg.bind('<Down>', self.handle_keypress)
-        dlg.bind('n', self.handle_keypress)
-        dlg.bind('m', self.handle_keypress)
-        dlg.bind('y', self.handle_keypress)
-        # Register callback that's called when window closes
-        dlg.wm_protocol('WM_DELETE_WINDOW', self._close_window)
-        # Init fields
-        self.node = None
-        self.node_stack = []
-        self.all_entries = []
-        self.shown_entries = []
-        self.config_path = None
-        self.unsaved_changes = False
-        self.status_string = 'NEW CONFIG'
-        self.update_status()
-        # Display first child of top level node (the top level node is 'mainmenu')
-        self.show_node(self.kconfig.top_node)
-
-    def _set_option_to_all_children(self, widget, option, value):
-        widget[option] = value
-        for n,c in widget.children.items():
-            self._set_option_to_all_children(c, option, value)
-
-    def _invert_colors(self, idx):
-        self.list.itemconfig(idx, {'bg' : self.list['foreground']})
-        self.list.itemconfig(idx, {'fg' : self.list['background']})
-
-    @property
-    def _selected_entry(self):
-        # type: (...) -> ListEntry
-        active_idx = self.list.index(tk.ACTIVE)
-        if active_idx >= 0 and active_idx < len(self.shown_entries):
-            return self.shown_entries[active_idx]
-        return None
-
-    def _select_node(self, node):
-        # type: (kconfiglib.MenuNode) -> None
-        """
-        Attempts to select entry that corresponds to given MenuNode in main listbox
-        """
-        idx = None
-        for i, e in enumerate(self.shown_entries):
-            if e.node is node:
-                idx = i
-                break
-        if idx is not None:
-            self.list.activate(idx)
-            self.list.see(idx)
-            self._invert_colors(idx)
-
-    def handle_keypress(self, ev):
-        keysym = ev.keysym
-        if keysym == 'Left':
-            self._select_action(prev=True)
-        elif keysym == 'Right':
-            self._select_action(prev=False)
-        elif keysym == 'Up':
-            self.refresh_display(reset_selection=False)
-        elif keysym == 'Down':
-            self.refresh_display(reset_selection=False)
-        elif keysym == 'space':
-            self._selected_entry.toggle()
-        elif keysym in ('n', 'm', 'y'):
-            self._selected_entry.set_tristate_value(kconfiglib.STR_TO_TRI[keysym])
-        elif keysym == 'Return':
-            action = self.tk_selected_action.get()
-            if action == self.ACTION_SELECT:
-                self._selected_entry.select()
-            elif action == self.ACTION_EXIT:
-                self._action_exit()
-            elif action == self.ACTION_HELP:
-                self._selected_entry.show_help()
-            elif action == self.ACTION_LOAD:
-                if self.prevent_losing_changes():
-                    self.open_config()
-            elif action == self.ACTION_SAVE:
-                self.save_config()
-            elif action == self.ACTION_SAVE_AS:
-                self.save_config(force_file_dialog=True)
-        elif keysym == 'Escape':
-            self._action_exit()
-        pass
-
-    def _close_window(self):
-        if self.prevent_losing_changes():
-            print('Exiting..')
-            if self.__silent is True:
-                return
-            self.root.destroy()
-
-    def _action_exit(self):
-        if self.node_stack:
-            self.show_parent()
-        else:
-            self._close_window()
-
-    def _select_action(self, prev=False):
-        # Determine the radio button that's activated
-        action = self.tk_selected_action.get()
-        if prev:
-            action -= 1
-        else:
-            action += 1
-        action %= len(MenuConfig.ACTIONS)
-        self.tk_selected_action.set(action)
-
-    def _collect_list_entries(self, start_node, indent=0):
-        """
-        Given first MenuNode of nodes list at some level in menu hierarchy,
-        collects nodes that may be displayed when viewing and editing that
-        hierarchy level. Includes implicit menu nodes, i.e. the ones dependent
-        on 'config' entry via 'if' statement which are internally represented
-        as children of their dependency
-        """
-        entries = []
-        n = start_node
-        while n is not None:
-            entries.append(ListEntry(self, n, indent))
-            # If node refers to a symbol (X) and has children, it is either
-            # 'config' or 'menuconfig'. The children are items inside 'if X'
-            # block that immediately follows 'config' or 'menuconfig' entry.
-            # If it's a 'menuconfig' then corresponding MenuNode is shown as a
-            # regular menu entry. But if it's a 'config', then its children need
-            # to be shown in the same list with their texts indented
-            if (n.list is not None
-                and isinstance(n.item, kconfiglib.Symbol)
-                and n.is_menuconfig == False):
-                entries.extend(
-                    self._collect_list_entries(n.list, indent=indent + 1)
-                )
-            n = n.next
-        return entries
-
-    def refresh_display(self, reset_selection=False):
-        # Refresh list entries' attributes
-        for e in self.all_entries:
-            e.refresh()
-        # Try to preserve selection upon refresh
-        selected_entry = self._selected_entry
-        # Also try to preserve listbox scroll offset
-        # If not preserved, the see() method will make wanted item to appear
-        # at the bottom of the list, even if previously it was in center
-        scroll_offset = self.list.yview()[0]
-        # Show only visible entries
-        self.shown_entries = [e for e in self.all_entries if e.visible]
-        # Refresh listbox contents
-        self.list.delete(0, tk.END)
-        self.list.insert(0, *self.shown_entries)
-        if selected_entry and not reset_selection:
-            # Restore scroll position
-            self.list.yview_moveto(scroll_offset)
-            # Activate previously selected node
-            self._select_node(selected_entry.node)
-        else:
-            # Select the topmost entry
-            self.list.activate(0)
-            self._invert_colors(0)
-        # Select ACTION_SELECT on each refresh (mimic C menuconfig)
-        self.tk_selected_action.set(self.ACTION_SELECT)
-        # Display current location in configuration tree
-        pos = []
-        for n in self.node_stack + [self.node]:
-            pos.append(n.prompt[0] if n.prompt else '[none]')
-        self.label_position['text'] = u'# ' + u' -> '.join(pos)
-
-    def show_node(self, node):
-        self.node = node
-        if node.list is not None:
-            self.all_entries = self._collect_list_entries(node.list)
-        else:
-            self.all_entries = []
-        self.refresh_display(reset_selection=True)
-
-    def show_submenu(self, node):
-        self.node_stack.append(self.node)
-        self.show_node(node)
-
-    def show_parent(self):
-        if self.node_stack:
-            select_node = self.node
-            parent_node = self.node_stack.pop()
-            self.show_node(parent_node)
-            # Restore previous selection
-            self._select_node(select_node)
-            self.refresh_display(reset_selection=False)
-
-    def ask_for_string(self, ident=None, title='Enter string', value=None):
-        """
-        Raises dialog with text entry widget and asks user to enter string
-
-        Return:
-            - str - user entered string
-            - None - entry was cancelled
-        """
-        text = 'Please enter a string value\n' \
-               'User <Enter> key to accept the value\n' \
-               'Use <Esc> key to cancel entry\n'
-        d = EntryDialog(self.root, text, title, ident=ident, value=value)
-        self.root.wait_window(d.dlg)
-        self.list.focus_set()
-        return d.value
-
-    def ask_for_int(self, ident=None, title='Enter integer value', value=None, ranges=()):
-        """
-        Raises dialog with text entry widget and asks user to enter decimal number
-        Ranges should be iterable of tuples (start, end),
-        where 'start' and 'end' specify allowed value range (inclusively)
-
-        Return:
-            - int - when valid number that falls within any one of specified ranges is entered
-            - None - invalid number or entry was cancelled
-        """
-        text = 'Please enter a decimal value. Fractions will not be accepted\n' \
-               'User <Enter> key to accept the value\n' \
-               'Use <Esc> key to cancel entry\n'
-        d = EntryDialog(self.root, text, title, ident=ident, value=value)
-        self.root.wait_window(d.dlg)
-        self.list.focus_set()
-        ivalue = None
-        if d.value:
-            try:
-                ivalue = int(d.value)
-            except ValueError:
-                messagebox.showerror('Bad value', 'Entered value \'{}\' is not an integer'.format(d.value))
-            if ivalue is not None and ranges:
-                allowed = False
-                for start, end in ranges:
-                    allowed = allowed or start <= ivalue and ivalue <= end
-                if not allowed:
-                    messagebox.showerror(
-                        'Bad value',
-                        'Entered value \'{:d}\' is out of range\n'
-                        'Allowed:\n{}'.format(
-                            ivalue,
-                            '\n'.join(['  {:d} - {:d}'.format(s,e) for s,e in ranges])
-                        )
-                    )
-                    ivalue = None
-        return ivalue
-
-    def ask_for_hex(self, ident=None, title='Enter hexadecimal value', value=None, ranges=()):
-        """
-        Raises dialog with text entry widget and asks user to enter decimal number
-        Ranges should be iterable of tuples (start, end),
-        where 'start' and 'end' specify allowed value range (inclusively)
-
-        Return:
-            - int - when valid number that falls within any one of specified ranges is entered
-            - None - invalid number or entry was cancelled
-        """
-        text = 'Please enter a hexadecimal value\n' \
-               'User <Enter> key to accept the value\n' \
-               'Use <Esc> key to cancel entry\n'
-        d = EntryDialog(self.root, text, title, ident=ident, value=value)
-        self.root.wait_window(d.dlg)
-        self.list.focus_set()
-        hvalue = None
-        if d.value:
-            try:
-                hvalue = int(d.value, base=16)
-            except ValueError:
-                messagebox.showerror('Bad value', 'Entered value \'{}\' is not a hexadecimal value'.format(d.value))
-            if hvalue is not None and ranges:
-                allowed = False
-                for start, end in ranges:
-                    allowed = allowed or start <= hvalue and hvalue <= end
-                if not allowed:
-                    messagebox.showerror(
-                        'Bad value',
-                        'Entered value \'0x{:x}\' is out of range\n'
-                        'Allowed:\n{}'.format(
-                            hvalue,
-                            '\n'.join(['  0x{:x} - 0x{:x}'.format(s,e) for s,e in ranges])
-                        )
-                    )
-                    hvalue = None
-        return hvalue
-
-    def show_text(self, text, title='Info'):
-        """
-        Raises dialog with read-only text view that contains supplied text
-        """
-        d = TextDialog(self.root, text, title)
-        self.root.wait_window(d.dlg)
-        self.list.focus_set()
-
-    def mark_as_changed(self):
-        """
-        Marks current config as having unsaved changes
-        Should be called whenever config value is changed
-        """
-        self.unsaved_changes = True
-        self.update_status()
-
-    def set_status_string(self, status):
-        """
-        Sets status string displayed at the bottom of the window
-        """
-        self.status_string = status
-        self.update_status()
-
-    def update_status(self):
-        """
-        Updates status bar display
-        Status bar displays:
-        - unsaved status
-        - current config path
-        - status string (see set_status_string())
-        """
-        if self.__silent is True:
-            return
-        self.tk_status.set('{} [{}] {}'.format(
-            '<UNSAVED>' if self.unsaved_changes else '',
-            self.config_path if self.config_path else '',
-            self.status_string
-        ))
-
-    def _check_is_visible(self, node):
-        v = True
-        v = v and node.prompt is not None
-        # It should be enough to check if prompt expression is not false and
-        # for menu nodes whether 'visible if' is not false
-        v = v and kconfiglib.expr_value(node.prompt[1]) > 0
-        if node.item == kconfiglib.MENU:
-            v = v and kconfiglib.expr_value(node.visibility) > 0
-        # If node references Symbol, then we also account for symbol visibility
-        # TODO: need to re-think whether this is needed
-        if isinstance(node.item, kconfiglib.Symbol):
-            if node.item.type in (kconfiglib.BOOL, kconfiglib.TRISTATE):
-                v = v and len(node.item.assignable) > 0
-            else:
-                v = v and node.item.visibility > 0
-        return v
-
-    def config_is_changed(self):
-        is_changed = False
-        node = self.kconfig.top_node.list
-        if not node:
-            # Empty configuration
-            return is_changed
-
-        while 1:
-            item = node.item
-            if isinstance(item, kconfiglib.Symbol) and item.user_value is None and self._check_is_visible(node):
-                is_changed = True
-                print("Config \"# {}\" has changed, need save config file\n".format(node.prompt[0]))
-                break;
-
-            # Iterative tree walk using parent pointers
-
-            if node.list:
-                node = node.list
-            elif node.next:
-                node = node.next
-            else:
-                while node.parent:
-                    node = node.parent
-                    if node.next:
-                        node = node.next
-                        break
-                else:
-                    break
-        return is_changed
-
-    def prevent_losing_changes(self):
-        """
-        Checks if there are unsaved changes and asks user to save or discard them
-        This routine should be called whenever current config is going to be discarded
-
-        Raises the usual 'Yes', 'No', 'Cancel' prompt.
-
-        Return:
-            - True: caller may safely drop current config state
-            - False: user needs to continue work on current config ('Cancel' pressed or saving failed)
-        """
-        if self.config_is_changed() == True:
-            self.mark_as_changed()
-        if not self.unsaved_changes:
-            return True
-        
-        if self.__silent:
-            saved = self.save_config()
-            return saved
-        res = messagebox.askyesnocancel(
-            parent=self.root,
-            title='Unsaved changes',
-            message='Config has unsaved changes. Do you want to save them?'
-        )
-        if res is None:
-            return False
-        elif res is False:
-            return True
-        # Otherwise attempt to save config and succeed only if config has been saved successfully
-        saved = self.save_config()
-        return saved
-
-    def open_config(self, path=None):
-        if path is None:
-            # Create open dialog. Either existing file is selected or no file is selected as a result
-            path = filedialog.askopenfilename(
-                parent=self.root,
-                title='Open config..',
-                initialdir=os.path.dirname(self.config_path) if self.config_path else os.getcwd(),
-                filetypes=(('.config files', '*.config'), ('All files', '*.*'))
-            )
-            if not path or not os.path.isfile(path):
-                return False
-        path = os.path.abspath(path)
-        print('Loading config: \'{}\''.format(path))
-        # Try to open given path
-        # If path does not exist, we still set current config path to it but don't load anything
-        self.unsaved_changes = False
-        self.config_path = path
-        if not os.path.exists(path):
-            self.set_status_string('New config')
-            self.mark_as_changed()
-            return True
-        # Load config and set status accordingly
-        try:
-            self.kconfig.load_config(path)
-        except IOError as e:
-            self.set_status_string('Failed to load: \'{}\''.format(path))
-            if not self.__silent:
-                self.refresh_display()
-            print('Failed to load config \'{}\': {}'.format(path, e))
-            return False
-        self.set_status_string('Opened config')
-        if not self.__silent:
-            self.refresh_display()
-        return True
-
-    def save_config(self, force_file_dialog=False):
-        path = self.config_path
-        if path is None or force_file_dialog:
-            path = filedialog.asksaveasfilename(
-                parent=self.root,
-                title='Save config as..',
-                initialdir=os.path.dirname(self.config_path) if self.config_path else os.getcwd(),
-                initialfile=os.path.basename(self.config_path) if self.config_path else None,
-                defaultextension='.config',
-                filetypes=(('.config files', '*.config'), ('All files', '*.*'))
-            )
-        if not path:
-            return False
-        path = os.path.abspath(path)
-        print('Saving config: \'{}\''.format(path))
-        # Try to save config to selected path
-        try:
-            self.kconfig.write_config(path, header="#\n# Automatically generated file; DO NOT EDIT.\n")
-            self.unsaved_changes = False
-            self.config_path = path
-            self.set_status_string('Saved config')
-        except IOError as e:
-            self.set_status_string('Failed to save: \'{}\''.format(path))
-            print('Save failed: {}'.format(e), file=sys.stderr)
-            return False
-        return True
-
-
-def _center_window(root, window):
-    # type: (tk.Tk, tk.Toplevel) -> None
-    """
-    Attempts to center window on screen
-    """
-    root.update_idletasks()
-    # root.eval('tk::PlaceWindow {!s} center'.format(
-    #     window.winfo_pathname(window.winfo_id())
-    # ))
-    w = window.winfo_width()
-    h = window.winfo_height()
-    ws = window.winfo_screenwidth()
-    hs = window.winfo_screenheight()
-    x = (ws / 2) - (w / 2)
-    y = (hs / 2) - (h / 2)
-    window.geometry('+{:d}+{:d}'.format(int(x), int(y)))
-    window.lift()
-    window.focus_force()
-
-
-def _center_window_above_parent(root, window):
-    # type: (tk.Tk, tk.Toplevel) -> None
-    """
-    Attempts to center window above its parent window
-    """
-    # root.eval('tk::PlaceWindow {!s} center'.format(
-    #     window.winfo_pathname(window.winfo_id())
-    # ))
-    root.update_idletasks()
-    parent = window.master
-    w = window.winfo_width()
-    h = window.winfo_height()
-    px = parent.winfo_rootx()
-    py = parent.winfo_rooty()
-    pw = parent.winfo_width()
-    ph = parent.winfo_height()
-    x = px + (pw / 2) - (w / 2)
-    y = py + (ph / 2) - (h / 2)
-    window.geometry('+{:d}+{:d}'.format(int(x), int(y)))
-    window.lift()
-    window.focus_force()
-
-
-def main(argv=None):
-    if argv is None:
-        argv = sys.argv[1:]
-    # Instantiate cmd options parser
-    parser = argparse.ArgumentParser(
-        description='Interactive Kconfig configuration editor'
-    )
-    parser.add_argument(
-        '--kconfig',
-        metavar='FILE',
-        type=str,
-        default='Kconfig',
-        help='path to root Kconfig file'
-    )
-    parser.add_argument(
-        '--config',
-        metavar='FILE',
-        type=str,
-        help='path to .config file to load'
-    )
-    if "--silent" in argv:
-        parser.add_argument(
-            '--silent',
-            dest = '_silent_',
-            type=str,
-            help='silent mode, not show window'
-        )
-    args = parser.parse_args(argv)
-    kconfig_path = args.kconfig
-    config_path = args.config
-    # Verify that Kconfig file exists
-    if not os.path.isfile(kconfig_path):
-        raise RuntimeError('\'{}\': no such file'.format(kconfig_path))
-
-    # Parse Kconfig files
-    kconf = kconfiglib.Kconfig(filename=kconfig_path)
-
-    if "--silent" not in argv:
-        print("In normal mode. Will show menuconfig window.")
-        mc = MenuConfig(kconf)
-        # If config file was specified, load it
-        if config_path:
-            mc.open_config(config_path)
-
-        print("Enter mainloop. Waiting...")
-        tk.mainloop()
-    else:
-        print("In silent mode. Don`t show menuconfig window.")
-        mc = MenuConfig(kconf, True)
-        # If config file was specified, load it
-        if config_path:
-            mc.open_config(config_path)
-        mc._close_window()
-
-
-if __name__ == '__main__':
-    main()