File: windows.py

package info (click to toggle)
progressbar2 4.5.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 1,216 kB
  • sloc: python: 8,001; makefile: 155
file content (174 lines) | stat: -rw-r--r-- 4,491 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
# ruff: noqa: N801
"""
Windows specific code for the terminal.

Note that the naming convention here is non-pythonic because we are
matching the Windows API naming.
"""

from __future__ import annotations

import ctypes
import enum
from ctypes.wintypes import (
    BOOL as _BOOL,
    CHAR as _CHAR,
    DWORD as _DWORD,
    HANDLE as _HANDLE,
    SHORT as _SHORT,
    UINT as _UINT,
    WCHAR as _WCHAR,
    WORD as _WORD,
)

_kernel32 = ctypes.windll.Kernel32  # type: ignore

_STD_INPUT_HANDLE = _DWORD(-10)
_STD_OUTPUT_HANDLE = _DWORD(-11)


class WindowsConsoleModeFlags(enum.IntFlag):
    ENABLE_ECHO_INPUT = 0x0004
    ENABLE_EXTENDED_FLAGS = 0x0080
    ENABLE_INSERT_MODE = 0x0020
    ENABLE_LINE_INPUT = 0x0002
    ENABLE_MOUSE_INPUT = 0x0010
    ENABLE_PROCESSED_INPUT = 0x0001
    ENABLE_QUICK_EDIT_MODE = 0x0040
    ENABLE_WINDOW_INPUT = 0x0008
    ENABLE_VIRTUAL_TERMINAL_INPUT = 0x0200

    ENABLE_PROCESSED_OUTPUT = 0x0001
    ENABLE_WRAP_AT_EOL_OUTPUT = 0x0002
    ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004
    DISABLE_NEWLINE_AUTO_RETURN = 0x0008
    ENABLE_LVB_GRID_WORLDWIDE = 0x0010

    def __str__(self) -> str:
        return f'{self.name} (0x{self.value:04X})'


_GetConsoleMode = _kernel32.GetConsoleMode
_GetConsoleMode.restype = _BOOL

_SetConsoleMode = _kernel32.SetConsoleMode
_SetConsoleMode.restype = _BOOL

_GetStdHandle = _kernel32.GetStdHandle
_GetStdHandle.restype = _HANDLE

_ReadConsoleInput = _kernel32.ReadConsoleInputA
_ReadConsoleInput.restype = _BOOL

_h_console_input = _GetStdHandle(_STD_INPUT_HANDLE)
_input_mode = _DWORD()
_GetConsoleMode(_HANDLE(_h_console_input), ctypes.byref(_input_mode))

_h_console_output = _GetStdHandle(_STD_OUTPUT_HANDLE)
_output_mode = _DWORD()
_GetConsoleMode(_HANDLE(_h_console_output), ctypes.byref(_output_mode))


class _COORD(ctypes.Structure):
    _fields_ = (('X', _SHORT), ('Y', _SHORT))


class _FOCUS_EVENT_RECORD(ctypes.Structure):
    _fields_ = (('bSetFocus', _BOOL),)


class _KEY_EVENT_RECORD(ctypes.Structure):
    class _uchar(ctypes.Union):
        _fields_ = (('UnicodeChar', _WCHAR), ('AsciiChar', _CHAR))

    _fields_ = (
        ('bKeyDown', _BOOL),
        ('wRepeatCount', _WORD),
        ('wVirtualKeyCode', _WORD),
        ('wVirtualScanCode', _WORD),
        ('uChar', _uchar),
        ('dwControlKeyState', _DWORD),
    )


class _MENU_EVENT_RECORD(ctypes.Structure):
    _fields_ = (('dwCommandId', _UINT),)


class _MOUSE_EVENT_RECORD(ctypes.Structure):
    _fields_ = (
        ('dwMousePosition', _COORD),
        ('dwButtonState', _DWORD),
        ('dwControlKeyState', _DWORD),
        ('dwEventFlags', _DWORD),
    )


class _WINDOW_BUFFER_SIZE_RECORD(ctypes.Structure):
    _fields_ = (('dwSize', _COORD),)


class _INPUT_RECORD(ctypes.Structure):
    class _Event(ctypes.Union):
        _fields_ = (
            ('KeyEvent', _KEY_EVENT_RECORD),
            ('MouseEvent', _MOUSE_EVENT_RECORD),
            ('WindowBufferSizeEvent', _WINDOW_BUFFER_SIZE_RECORD),
            ('MenuEvent', _MENU_EVENT_RECORD),
            ('FocusEvent', _FOCUS_EVENT_RECORD),
        )

    _fields_ = (('EventType', _WORD), ('Event', _Event))


def reset_console_mode() -> None:
    _SetConsoleMode(_HANDLE(_h_console_input), _DWORD(_input_mode.value))
    _SetConsoleMode(_HANDLE(_h_console_output), _DWORD(_output_mode.value))


def set_console_mode() -> bool:
    mode = (
        _input_mode.value
        | WindowsConsoleModeFlags.ENABLE_VIRTUAL_TERMINAL_INPUT
    )
    _SetConsoleMode(_HANDLE(_h_console_input), _DWORD(mode))

    mode = (
        _output_mode.value
        | WindowsConsoleModeFlags.ENABLE_PROCESSED_OUTPUT
        | WindowsConsoleModeFlags.ENABLE_VIRTUAL_TERMINAL_PROCESSING
    )
    return bool(_SetConsoleMode(_HANDLE(_h_console_output), _DWORD(mode)))


def get_console_mode() -> int:
    return _input_mode.value


def set_text_color(color) -> None:
    _kernel32.SetConsoleTextAttribute(_h_console_output, color)


def print_color(text, color) -> None:
    set_text_color(color)
    print(text)  # noqa: T201
    set_text_color(7)  # Reset to default color, grey


def getch():
    lp_buffer = (_INPUT_RECORD * 2)()
    n_length = _DWORD(2)
    lp_number_of_events_read = _DWORD()

    _ReadConsoleInput(
        _HANDLE(_h_console_input),
        lp_buffer,
        n_length,
        ctypes.byref(lp_number_of_events_read),
    )

    char = lp_buffer[1].Event.KeyEvent.uChar.AsciiChar.decode('ascii')
    if char == '\x00':
        return None

    return char