diff --git a/src/get_args.py b/src/get_args.py index 3966759..a79f911 100644 --- a/src/get_args.py +++ b/src/get_args.py @@ -5,12 +5,14 @@ from typing import Any try: - from curses import wrapper # pyright: ignore + # from curses import wrapper # pyright: ignore + from src.working_initscr import wrapper # pyright: ignore _DEFAULT_TKINTER_GUI = False except ImportError: - def wrapper(_: Any) -> str: # pylint: disable=missing-function-docstring + def wrapper(_: Any, *args: Any, **kwargs: Any) -> Any: # pylint: disable=missing-function-docstring + _ = (args, kwargs) return _CHECKBOX_OPTIONS[1] _DEFAULT_TKINTER_GUI = True # pyright: ignore[reportConstantRedefinition] diff --git a/src/working_initscr.py b/src/working_initscr.py new file mode 100644 index 0000000..e83f1aa --- /dev/null +++ b/src/working_initscr.py @@ -0,0 +1,54 @@ +""" +For some reason curses initscr doesn't work on python3.12, +presumably due to a compilation bug or something. This +module provides a functional workaround. + +Thanks to https://github.com/zephyrproject-rtos/windows-curses/issues/50 +for the inspiration. +""" + +import curses +from typing import Any, Callable + +import _curses + + +def initscr() -> Any: + """Return a stdscr that should be properly initialized""" + stdscr = _curses.initscr() # pyright: ignore + for key, value in _curses.__dict__.items(): + if key[0:4] == "ACS_" or key in ("LINES", "COLS"): + setattr(curses, key, value) + return stdscr # pyright: ignore + + +def wrapper(func: Callable[..., Any], /, *args: Any, **kwds: Any) -> Any: + """ + Wrapper function that initializes curses and calls another function, + restoring normal keyboard/screen behavior on error. + The callable object 'func' is then passed the main window 'stdscr' + as its first argument, followed by any other arguments passed to + wrapper(). + """ + + try: + stdscr = initscr() + + curses.noecho() + curses.cbreak() + + stdscr.keypad(1) + + _curses.start_color() # pyright: ignore + if hasattr(_curses, 'COLORS'): + curses.COLORS = _curses.COLORS # pyright: ignore + if hasattr(_curses, 'COLOR_PAIRS'): + curses.COLOR_PAIRS = _curses.COLOR_PAIRS # pyright: ignore + + return func(stdscr, *args, **kwds) + finally: + if 'stdscr' in locals(): + stdscr.keypad(0) # pyright: ignore + curses.echo() + curses.nocbreak() + curses.endwin() diff --git a/todo.py b/todo.py index 360e2bb..c6ead64 100755 --- a/todo.py +++ b/todo.py @@ -632,4 +632,6 @@ def main(stdscr: Any) -> int: print(f"{HEADER}:") print_todos(None, file_string_to_todos(read_file(FILENAME)), Cursor(0)) sys_exit() - curses.wrapper(main) + + from src.working_initscr import wrapper # pylint: disable=ungrouped-imports + wrapper(main) diff --git a/todo.txt b/todo.txt index 31dd26f..7b6b27a 100644 --- a/todo.txt +++ b/todo.txt @@ -1,8 +1,6 @@ -1 fix strikethrough -1 fix long lines (longer than width // 2 - 1) -1 fix moving down onto a toggled line --3 make it work on windows - -3 appears to be a compatibility problem with python3.12 -4 implement multiline todos -4 allow multiline paste -4 allow multiline print