1"""
2Utilities for working with terminals.
3
4Authors:
5
6* Brian E. Granger
7* Fernando Perez
8* Alexander Belchenko (e-mail: bialix AT ukr.net)
9"""
10from __future__ import annotations
11
12# Copyright (c) IPython Development Team.
13# Distributed under the terms of the Modified BSD License.
14
15import os
16import re
17import sys
18import warnings
19
20# This variable is part of the expected API of the module:
21ignore_termtitle = True
22
23# C0/C1 controls and DEL. These terminate or abort the OSC string used to set
24# the title, leaving the rest of it to be read as a new terminal command.
25_title_controls_re = re.compile(r"[\x00-\x1f\x7f-\x9f]")
26
27
28
29if os.name == 'posix':
30 def _term_clear():
31 os.system('clear')
32elif sys.platform == 'win32':
33 def _term_clear():
34 os.system('cls')
35else:
36 def _term_clear():
37 pass
38
39
40
41def toggle_set_term_title(val: bool):
42 """Control whether set_term_title is active or not.
43
44 set_term_title() allows writing to the console titlebar. In embedded
45 widgets this can cause problems, so this call can be used to toggle it on
46 or off as needed.
47
48 The default state of the module is for the function to be disabled.
49
50 Parameters
51 ----------
52 val : bool
53 If True, set_term_title() actually writes to the terminal (using the
54 appropriate platform-specific module). If False, it is a no-op.
55 """
56 global ignore_termtitle
57 ignore_termtitle = not(val)
58
59
60def _set_term_title(title: str) -> None:
61 """Dummy no-op."""
62 pass
63
64
65def _restore_term_title():
66 pass
67
68
69_xterm_term_title_saved = False
70
71
72def _set_term_title_xterm(title: str) -> None:
73 """ Change virtual terminal title in xterm-workalikes """
74 global _xterm_term_title_saved
75 # Only save the title the first time we set, otherwise restore will only
76 # go back one title (probably undoing a %cd title change).
77 if not _xterm_term_title_saved:
78 # save the current title to the xterm "stack"
79 sys.stdout.write("\033[22;0t")
80 _xterm_term_title_saved = True
81 sys.stdout.write("\033]0;%s\007" % _title_controls_re.sub("", title))
82
83
84def _restore_term_title_xterm():
85 # Make sure the restore has at least one accompanying set.
86 global _xterm_term_title_saved
87 if not _xterm_term_title_saved:
88 warnings.warn(
89 "Expecting xterm_term_title_saved to be True, but is not; will not restore terminal title.",
90 stacklevel=1,
91 )
92 return
93
94 sys.stdout.write("\033[23;0t")
95 _xterm_term_title_saved = False
96
97
98if os.name == 'posix':
99 TERM = os.environ.get('TERM','')
100 if TERM.startswith('xterm'):
101 _set_term_title = _set_term_title_xterm
102 _restore_term_title = _restore_term_title_xterm
103elif sys.platform == 'win32':
104 import ctypes
105
106 SetConsoleTitleW = ctypes.windll.kernel32.SetConsoleTitleW
107 SetConsoleTitleW.argtypes = [ctypes.c_wchar_p]
108
109 def _set_term_title(title: str) -> None:
110 """Set terminal title using ctypes to access the Win32 APIs."""
111 SetConsoleTitleW(title)
112
113
114def set_term_title(title: str) -> None:
115 """Set terminal title using the necessary platform-dependent calls."""
116 if ignore_termtitle:
117 return
118 _set_term_title(title)
119
120
121def restore_term_title():
122 """Restore, if possible, terminal title to the original state"""
123 if ignore_termtitle:
124 return
125 _restore_term_title()
126
127
128def get_terminal_size(defaultx: int = 80, defaulty: int = 25) -> tuple[int, int]:
129 from shutil import get_terminal_size as _get_terminal_size
130 return _get_terminal_size((defaultx, defaulty))