boacon 0.0.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ **/__pycache__
boacon-0.0.1/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Zachary Aaron Combs
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
boacon-0.0.1/PKG-INFO ADDED
@@ -0,0 +1,145 @@
1
+ Metadata-Version: 2.4
2
+ Name: boacon
3
+ Version: 0.0.1
4
+ Project-URL: Homepage, https://github.com/blackrookgames/boacon
5
+ Project-URL: Issues, https://github.com/blackrookgames/boacon/issues
6
+ Author-email: Black Rook Games <blackrookgames@gmail.com>
7
+ License-Expression: MIT
8
+ License-File: LICENSE
9
+ Classifier: Operating System :: OS Independent
10
+ Classifier: Programming Language :: Python :: 3
11
+ Requires-Python: >=3.13
12
+ Requires-Dist: numpy>=2.4.1
13
+ Requires-Dist: windows-curses>=2.4.1; sys_platform == 'win32'
14
+ Description-Content-Type: text/markdown
15
+
16
+ <a id="boacon"></a>
17
+
18
+ # boacon
19
+
20
+ <a id="boacon.init"></a>
21
+
22
+ #### init
23
+
24
+ ```python
25
+ def init()
26
+ ```
27
+
28
+ <a id="boacon.final"></a>
29
+
30
+ #### final
31
+
32
+ ```python
33
+ def final()
34
+ ```
35
+
36
+ <a id="boacon.state"></a>
37
+
38
+ #### state
39
+
40
+ ```python
41
+ def state()
42
+ ```
43
+
44
+ State of the boacon system
45
+
46
+ <a id="boacon.panes"></a>
47
+
48
+ #### panes
49
+
50
+ ```python
51
+ def panes()
52
+ ```
53
+
54
+ Panes that are being displayed
55
+
56
+ :raise BCError:
57
+ boacon system is not currently running
58
+
59
+ <a id="boacon.on_init"></a>
60
+
61
+ #### on\_init
62
+
63
+ ```python
64
+ def on_init()
65
+ ```
66
+
67
+ Emitted after the boacon system is initialized
68
+
69
+ <a id="boacon.on_final"></a>
70
+
71
+ #### on\_final
72
+
73
+ ```python
74
+ def on_final()
75
+ ```
76
+
77
+ Emitted before the boacon system is finalized
78
+
79
+ <a id="boacon.postdraw"></a>
80
+
81
+ #### postdraw
82
+
83
+ ```python
84
+ def postdraw()
85
+ ```
86
+
87
+ Emitted after drawing the panes and right before the screen is refreshed
88
+
89
+ :raise BCError:
90
+ boacon system is not currently running
91
+
92
+ <a id="boacon.refresh"></a>
93
+
94
+ #### refresh
95
+
96
+ ```python
97
+ def refresh()
98
+ ```
99
+
100
+ Refreshes the screen
101
+
102
+ :raise BCError:
103
+ boacon system is not currently running
104
+
105
+ <a id="boacon.getch"></a>
106
+
107
+ #### getch
108
+
109
+ ```python
110
+ def getch()
111
+ ```
112
+
113
+ Gets a character code from the keyboard
114
+
115
+ :return:
116
+ Character code (or -1 if no character is pressed)
117
+ :raise BCError:
118
+ boacon system is not currently running
119
+
120
+ <a id="boacon.get_border"></a>
121
+
122
+ #### get\_border
123
+
124
+ ```python
125
+ def get_border()
126
+ ```
127
+
128
+ Gets whether or not pane borders are enabled
129
+
130
+ :raise BCError:
131
+ boacon system is not currently running
132
+
133
+ <a id="boacon.set_border"></a>
134
+
135
+ #### set\_border
136
+
137
+ ```python
138
+ def set_border(value: bool)
139
+ ```
140
+
141
+ Sets whether or not pane borders are enabled
142
+
143
+ :raise BCError:
144
+ boacon system is not currently running
145
+
boacon-0.0.1/README.md ADDED
@@ -0,0 +1,130 @@
1
+ <a id="boacon"></a>
2
+
3
+ # boacon
4
+
5
+ <a id="boacon.init"></a>
6
+
7
+ #### init
8
+
9
+ ```python
10
+ def init()
11
+ ```
12
+
13
+ <a id="boacon.final"></a>
14
+
15
+ #### final
16
+
17
+ ```python
18
+ def final()
19
+ ```
20
+
21
+ <a id="boacon.state"></a>
22
+
23
+ #### state
24
+
25
+ ```python
26
+ def state()
27
+ ```
28
+
29
+ State of the boacon system
30
+
31
+ <a id="boacon.panes"></a>
32
+
33
+ #### panes
34
+
35
+ ```python
36
+ def panes()
37
+ ```
38
+
39
+ Panes that are being displayed
40
+
41
+ :raise BCError:
42
+ boacon system is not currently running
43
+
44
+ <a id="boacon.on_init"></a>
45
+
46
+ #### on\_init
47
+
48
+ ```python
49
+ def on_init()
50
+ ```
51
+
52
+ Emitted after the boacon system is initialized
53
+
54
+ <a id="boacon.on_final"></a>
55
+
56
+ #### on\_final
57
+
58
+ ```python
59
+ def on_final()
60
+ ```
61
+
62
+ Emitted before the boacon system is finalized
63
+
64
+ <a id="boacon.postdraw"></a>
65
+
66
+ #### postdraw
67
+
68
+ ```python
69
+ def postdraw()
70
+ ```
71
+
72
+ Emitted after drawing the panes and right before the screen is refreshed
73
+
74
+ :raise BCError:
75
+ boacon system is not currently running
76
+
77
+ <a id="boacon.refresh"></a>
78
+
79
+ #### refresh
80
+
81
+ ```python
82
+ def refresh()
83
+ ```
84
+
85
+ Refreshes the screen
86
+
87
+ :raise BCError:
88
+ boacon system is not currently running
89
+
90
+ <a id="boacon.getch"></a>
91
+
92
+ #### getch
93
+
94
+ ```python
95
+ def getch()
96
+ ```
97
+
98
+ Gets a character code from the keyboard
99
+
100
+ :return:
101
+ Character code (or -1 if no character is pressed)
102
+ :raise BCError:
103
+ boacon system is not currently running
104
+
105
+ <a id="boacon.get_border"></a>
106
+
107
+ #### get\_border
108
+
109
+ ```python
110
+ def get_border()
111
+ ```
112
+
113
+ Gets whether or not pane borders are enabled
114
+
115
+ :raise BCError:
116
+ boacon system is not currently running
117
+
118
+ <a id="boacon.set_border"></a>
119
+
120
+ #### set\_border
121
+
122
+ ```python
123
+ def set_border(value: bool)
124
+ ```
125
+
126
+ Sets whether or not pane borders are enabled
127
+
128
+ :raise BCError:
129
+ boacon system is not currently running
130
+
@@ -0,0 +1,49 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "boacon"
7
+ version = "0.0.1"
8
+ description = ""
9
+ readme = "README.md"
10
+ requires-python = ">=3.13"
11
+ license = "MIT"
12
+ license-files = ["LICEN[CS]E*"]
13
+ authors = [
14
+ { name = "Black Rook Games", email = "blackrookgames@gmail.com" },
15
+ ]
16
+ classifiers = [
17
+ "Programming Language :: Python :: 3",
18
+ "Operating System :: OS Independent",
19
+ ]
20
+ dependencies = [
21
+ "numpy >= 2.4.1",
22
+ "windows_curses >= 2.4.1 ; sys_platform == 'win32'",
23
+ ]
24
+
25
+ [project.urls]
26
+ Homepage = "https://github.com/blackrookgames/boacon"
27
+ Issues = "https://github.com/blackrookgames/boacon/issues"
28
+
29
+ [tool.hatch.build]
30
+ exclude = [
31
+ "*.code-workspace",
32
+ ".git/",
33
+ "/misc/",
34
+ "/tests/",
35
+ "/README.py",
36
+ ]
37
+
38
+ [[tool.pydoc-markdown.loaders]]
39
+ type = "python"
40
+ search_path = [ "src" ]
41
+ modules = [ "boacon" ]
42
+
43
+ [[tool.pydoc-markdown.processors]]
44
+ type = "filter"
45
+ expression = "(name == '__init__' or (len(name) > 0 and name[0].isalpha())) and default()"
46
+ documented_only = false
47
+
48
+ [tool.pydoc-markdown.renderer]
49
+ type = "markdown"
@@ -0,0 +1,360 @@
1
+ from .c_BCChar import *
2
+ from .c_BCCoord import *
3
+ from .c_BCError import *
4
+ from .c_BCPane import *
5
+ from .c_BCPostDrawArgs import *
6
+ from .c_BCSetChrFunc import *
7
+ from .c_BCSignal import *
8
+ from .c_BCSignalEmitter import *
9
+ from .c_BCState import *
10
+ from .c_BCStr import *
11
+ from .g_attr import *
12
+ from .g_key import *
13
+ from .p_BCConsolePane import *
14
+
15
+ import curses as _curses
16
+ import numpy as _np
17
+ import numpy.typing as _npt
18
+
19
+ #region const
20
+
21
+ _C_BORDERCHARS = [\
22
+ 0xFF, 0x50, 0x50, 0x50, 0x51, 0x54, 0x5A, 0x50, 0x51, 0x57, 0x5D, 0x50, 0x51, 0x51, 0x51, 0xA1,\
23
+ 0x5D, 0x50, 0x69, 0x50, 0x51, 0x54, 0x5A, 0x50, 0x63, 0x57, 0x5D, 0x50, 0x51, 0x51, 0x51, 0xA1,\
24
+ 0x5A, 0x50, 0x69, 0x50, 0x60, 0x54, 0x5A, 0x50, 0x51, 0x57, 0x5D, 0x50, 0x51, 0x51, 0x51, 0xA1,\
25
+ 0x69, 0x50, 0x69, 0x50, 0x60, 0x54, 0x5A, 0x50, 0x63, 0x57, 0x5D, 0x50, 0x51, 0x51, 0x51, 0xA1,\
26
+ 0x57, 0x66, 0x50, 0x50, 0x51, 0x54, 0x5A, 0x50, 0x63, 0x57, 0x5D, 0x50, 0x51, 0x51, 0x51, 0xA1,\
27
+ 0x63, 0x66, 0x69, 0x50, 0x51, 0x54, 0x5A, 0x50, 0x63, 0x57, 0x5D, 0x50, 0x51, 0x51, 0x51, 0xA1,\
28
+ 0x6C, 0x66, 0x69, 0x50, 0x60, 0x54, 0x5A, 0x50, 0x63, 0x57, 0x5D, 0x50, 0x51, 0x51, 0x51, 0xA1,\
29
+ 0x6C, 0x66, 0x69, 0x50, 0x60, 0x54, 0x5A, 0x50, 0x63, 0x57, 0x5D, 0x50, 0x51, 0x51, 0x51, 0xA1,\
30
+ 0x54, 0x66, 0x50, 0x50, 0x60, 0x54, 0x5A, 0x50, 0x51, 0x57, 0x5D, 0x50, 0x51, 0x51, 0x51, 0xA1,\
31
+ 0x6C, 0x66, 0x69, 0x50, 0x60, 0x54, 0x5A, 0x50, 0x63, 0x57, 0x5D, 0x50, 0x51, 0x51, 0x51, 0xA1,\
32
+ 0x60, 0x66, 0x69, 0x50, 0x60, 0x54, 0x5A, 0x50, 0x51, 0x57, 0x5D, 0x50, 0x51, 0x51, 0x51, 0xA1,\
33
+ 0x6C, 0x66, 0x69, 0x50, 0x60, 0x54, 0x5A, 0x50, 0x63, 0x57, 0x5D, 0x50, 0x51, 0x51, 0x51, 0xA1,\
34
+ 0x66, 0x66, 0x50, 0x50, 0x60, 0x54, 0x5A, 0x50, 0x63, 0x57, 0x5D, 0x50, 0x51, 0x51, 0x51, 0xA1,\
35
+ 0x6C, 0x66, 0x69, 0x50, 0x60, 0x54, 0x5A, 0x50, 0x63, 0x57, 0x5D, 0x50, 0x51, 0x51, 0x51, 0xA1,\
36
+ 0x6C, 0x66, 0x69, 0x50, 0x60, 0x54, 0x5A, 0x50, 0x63, 0x57, 0x5D, 0x50, 0x51, 0x51, 0x51, 0xA1,\
37
+ 0x6C, 0x66, 0x69, 0x50, 0x60, 0x54, 0x5A, 0x50, 0x63, 0x57, 0x5D, 0x50, 0x51, 0x51, 0x51, 0xA1,]
38
+
39
+ #endregion
40
+
41
+ #region variables
42
+
43
+ _f_state = BCState.NORUN
44
+ _f_win:None|_curses.window = None
45
+ _f_win_ok = False
46
+ _f_win_w = 0
47
+ _f_win_h = 0
48
+ _f_color = False
49
+ _f_panes:None|list[BCPane] = None
50
+ _f_bgbuffer:None|_npt.NDArray[_np.bool_] = None
51
+
52
+ _f_border = False
53
+
54
+ _s_on_init_emitter = BCSignalEmitter[()]()
55
+ _s_on_init = BCSignal(_s_on_init_emitter)
56
+ _s_on_final_emitter = BCSignalEmitter[()]()
57
+ _s_on_final = BCSignal(_s_on_final_emitter)
58
+
59
+ _s_postdraw_emitter:None|BCSignalEmitter[BCPostDrawArgs] = None
60
+ _s_postdraw:None|BCSignal[BCPostDrawArgs] = None
61
+
62
+ #endregion
63
+
64
+ #region helper
65
+
66
+ def _m_verify_run():
67
+ global _f_state
68
+ if _f_state == BCState.RUN: return
69
+ raise BCError("boacon system is not currently running.")
70
+
71
+ def _m_setcursor(visible:bool):
72
+ global _f_win, _f_win_ok
73
+ assert _f_win is not None
74
+ # Try to hide normally
75
+ if not _f_win_ok:
76
+ try: _curses.curs_set(1 if visible else 0)
77
+ except _curses.error: _f_win_ok = True
78
+ # Try to hide alternatively
79
+ if _f_win_ok: # Do NOT change to else or elif
80
+ try: _f_win.leaveok(not visible)
81
+ except _curses.error: pass
82
+
83
+ def _m_cursesattr_color(attr:int):
84
+ cattr = _curses.A_STANDOUT if attr_emp(attr) else _curses.A_NORMAL
85
+ return cattr | _curses.color_pair(0b111 ^ attr_color(attr))
86
+
87
+ def _m_cursesattr_nocolor(attr:int):
88
+ return _curses.A_STANDOUT if attr_emp(attr) else _curses.A_NORMAL
89
+
90
+ def _m_setchr_color(x:int, y:int, char:BCChar):
91
+ global _f_win, _f_bgbuffer, _f_win_w
92
+ assert _f_win is not None
93
+ assert _f_bgbuffer is not None
94
+ attr = _m_cursesattr_color(char.attr)
95
+ try:
96
+ _f_win.addch(y, x, chr(char.ord), attr)
97
+ _f_bgbuffer[y * _f_win_w + x] = True
98
+ except: pass
99
+
100
+ def _m_setchr_nocolor(x:int, y:int, char:BCChar):
101
+ global _f_win, _f_bgbuffer, _f_win_w
102
+ assert _f_win is not None
103
+ assert _f_bgbuffer is not None
104
+ attr = _m_cursesattr_nocolor(char.attr)
105
+ try:
106
+ _f_win.addch(y, x, chr(char.ord), attr)
107
+ _f_bgbuffer[y * _f_win_w + x] = True
108
+ except: pass
109
+
110
+ def _m_borderchar(x:int, y:int):
111
+ global _f_win_w, _f_win_h, _f_bgbuffer
112
+ assert _f_bgbuffer is not None
113
+ # Examine neighbors
114
+ index = y * _f_win_w + x
115
+ index_t = index - _f_win_w
116
+ index_b = index + _f_win_w
117
+ notedge_t = y > 0
118
+ notedge_b = (y + 1) < _f_win_h
119
+ notedge_l = x > 0
120
+ notedge_r = (x + 1) < _f_win_w
121
+ if notedge_t:
122
+ neighbor_t = _f_bgbuffer[index_t]
123
+ neighbor_tl = notedge_l and _f_bgbuffer[index_t - 1]
124
+ neighbor_tr = notedge_r and _f_bgbuffer[index_t + 1]
125
+ else:
126
+ neighbor_t = False
127
+ neighbor_tl = False
128
+ neighbor_tr = False
129
+ if notedge_b:
130
+ neighbor_b = _f_bgbuffer[index_b]
131
+ neighbor_bl = notedge_l and _f_bgbuffer[index_b - 1]
132
+ neighbor_br = notedge_r and _f_bgbuffer[index_b + 1]
133
+ else:
134
+ neighbor_b = False
135
+ neighbor_bl = False
136
+ neighbor_br = False
137
+ neighbor_l = notedge_l and _f_bgbuffer[index - 1]
138
+ neighbor_r = notedge_r and _f_bgbuffer[index + 1]
139
+ # Determine character
140
+ index = 0
141
+ if neighbor_t: index |= 0b00000001
142
+ if neighbor_b: index |= 0b00000010
143
+ if neighbor_l: index |= 0b00000100
144
+ if neighbor_r: index |= 0b00001000
145
+ if neighbor_tl: index |= 0b00010000
146
+ if neighbor_tr: index |= 0b00100000
147
+ if neighbor_bl: index |= 0b01000000
148
+ if neighbor_br: index |= 0b10000000
149
+ raw = _C_BORDERCHARS[index]
150
+ return ' ' if (raw == 0xFF) else chr(0x2500 | raw)
151
+
152
+ #endregion
153
+
154
+ #region init/final
155
+
156
+ def init():
157
+ global _f_state
158
+ if _f_state != BCState.NORUN: return
159
+ _f_state = BCState.INIT
160
+ # Global vars
161
+ global _f_win, _f_win_w, _f_win_h, _f_color, _f_panes
162
+ global _f_border
163
+ # Initialize window
164
+ _f_win = _curses.initscr()
165
+ _f_win.keypad(True)
166
+ _f_win.nodelay(True)
167
+ _f_win_w = 0
168
+ _f_win_h = 0
169
+ # Initialize curses
170
+ _curses.noecho()
171
+ _curses.cbreak()
172
+ _m_setcursor(False)
173
+ # Initialize color
174
+ _curses.start_color()
175
+ if _curses.has_colors() and _curses.COLOR_PAIRS >= 8:
176
+ _curses.use_default_colors()
177
+ _curses.init_pair(0b001, _curses.COLOR_CYAN, -1)
178
+ _curses.init_pair(0b010, _curses.COLOR_MAGENTA, -1)
179
+ _curses.init_pair(0b011, _curses.COLOR_BLUE, -1)
180
+ _curses.init_pair(0b100, _curses.COLOR_YELLOW, -1)
181
+ _curses.init_pair(0b101, _curses.COLOR_GREEN, -1)
182
+ _curses.init_pair(0b110, _curses.COLOR_RED, -1)
183
+ _curses.init_pair(0b111, _curses.COLOR_BLACK, -1)
184
+ _f_color = True
185
+ else:
186
+ _f_color = False
187
+ # Initialize panes
188
+ _f_panes = []
189
+ # Initialize border
190
+ _f_border = True
191
+ # Initialize signals
192
+ global _s_postdraw, _s_postdraw_emitter
193
+ _s_postdraw_emitter = BCSignalEmitter[BCPostDrawArgs]()
194
+ _s_postdraw = BCSignal[BCPostDrawArgs](_s_postdraw_emitter)
195
+ # Success!!!
196
+ _f_state = BCState.RUN
197
+ _s_on_init_emitter.emit(())
198
+
199
+ def final():
200
+ global _f_state
201
+ if _f_state != BCState.RUN: return
202
+ _s_on_final_emitter.emit(())
203
+ _f_state = BCState.FINAL
204
+ # Global vars
205
+ global _f_win, _panes
206
+ assert _f_win is not None
207
+ # Finalize curses
208
+ _curses.nocbreak()
209
+ _curses.echo()
210
+ _curses.endwin()
211
+ # Finalize window
212
+ _m_setcursor(True)
213
+ _f_win.nodelay(False)
214
+ _f_win.keypad(False)
215
+ # Success!!!
216
+ _f_state = BCState.NORUN
217
+
218
+ #endregion
219
+
220
+ #region "properties"
221
+
222
+ def state():
223
+ """
224
+ State of the boacon system
225
+ """
226
+ global _f_state
227
+ return _f_state
228
+
229
+ def panes():
230
+ """
231
+ Panes that are being displayed
232
+
233
+ :raise BCError:
234
+ boacon system is not currently running
235
+ """
236
+ _m_verify_run()
237
+ global _f_panes
238
+ assert _f_panes is not None
239
+ return _f_panes
240
+
241
+ def on_init():
242
+ """
243
+ Emitted after the boacon system is initialized
244
+ """
245
+ global _s_on_init
246
+ return _s_on_init
247
+
248
+ def on_final():
249
+ """
250
+ Emitted before the boacon system is finalized
251
+ """
252
+ global _s_on_final
253
+ return _s_on_final
254
+
255
+ def postdraw():
256
+ """
257
+ Emitted after drawing the panes and right before the screen is refreshed
258
+
259
+ :raise BCError:
260
+ boacon system is not currently running
261
+ """
262
+ _m_verify_run()
263
+ global _s_postdraw
264
+ assert _s_postdraw is not None
265
+ return _s_postdraw
266
+
267
+ #endregion
268
+
269
+ #region functions
270
+
271
+ def refresh():
272
+ """
273
+ Refreshes the screen
274
+
275
+ :raise BCError:
276
+ boacon system is not currently running
277
+ """
278
+ _m_verify_run()
279
+ global _f_win, _f_win_w, _f_win_h, _f_color, _f_panes, _f_bgbuffer
280
+ global _f_border
281
+ assert _f_win is not None
282
+ assert _f_panes is not None
283
+ global _s_postdraw_emitter
284
+ assert _s_postdraw_emitter is not None
285
+ # Determine functions
286
+ _func_cursesattr = _m_cursesattr_color if _f_color else _m_cursesattr_nocolor
287
+ _func_setchr = _m_setchr_color if _f_color else _m_setchr_nocolor
288
+ # Update size
289
+ _new_h, _new_w = _f_win.getmaxyx()
290
+ if _f_win_w != _new_w or _f_win_h != _new_h:
291
+ _f_win_w = _new_w
292
+ _f_win_h = _new_h
293
+ _f_bgbuffer = _np.zeros(_f_win_w * _f_win_h, dtype = bool)
294
+ resized = True
295
+ else:
296
+ assert _f_bgbuffer is not None
297
+ resized = False
298
+ # Resolve pane dimensions
299
+ redrawbg = resized
300
+ for _pane in _f_panes:
301
+ if _pane._m_resolve(resized, _f_win_w, _f_win_h):
302
+ redrawbg = True
303
+ if redrawbg:
304
+ _f_bgbuffer.fill(False)
305
+ # Update panes
306
+ for _pane in _f_panes:
307
+ _pane._m_refresh(redrawbg, _func_setchr)
308
+ # Draw background
309
+ if redrawbg:
310
+ for _y in range(_f_win_h):
311
+ for _x in range(_f_win_w):
312
+ _i = _y * _f_win_w + _x
313
+ if _f_bgbuffer[_i]: continue
314
+ # Determine character
315
+ _chr = _m_borderchar(_x, _y) if _f_border else ' '
316
+ # Set character
317
+ try: _f_win.addch(_y, _x, _chr)
318
+ except: pass
319
+ # Post draw
320
+ _s_postdraw_emitter.emit((BCPostDrawArgs(_f_win, _func_cursesattr, _func_setchr),))
321
+ # Success!!!
322
+ _f_win.refresh()
323
+
324
+ def getch():
325
+ """
326
+ Gets a character code from the keyboard
327
+
328
+ :return:
329
+ Character code (or -1 if no character is pressed)
330
+ :raise BCError:
331
+ boacon system is not currently running
332
+ """
333
+ _m_verify_run()
334
+ global _f_win
335
+ assert _f_win is not None
336
+ return _f_win.getch()
337
+
338
+ def get_border():
339
+ """
340
+ Gets whether or not pane borders are enabled
341
+
342
+ :raise BCError:
343
+ boacon system is not currently running
344
+ """
345
+ _m_verify_run()
346
+ global _f_border
347
+ return _f_border
348
+
349
+ def set_border(value:bool):
350
+ """
351
+ Sets whether or not pane borders are enabled
352
+
353
+ :raise BCError:
354
+ boacon system is not currently running
355
+ """
356
+ _m_verify_run()
357
+ global _f_border
358
+ _f_border = value
359
+
360
+ #endregion