mirror of https://github.com/H-uru/korman.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
155 lines
5.8 KiB
155 lines
5.8 KiB
# This file is part of Korman. |
|
# |
|
# Korman is free software: you can redistribute it and/or modify |
|
# it under the terms of the GNU General Public License as published by |
|
# the Free Software Foundation, either version 3 of the License, or |
|
# (at your option) any later version. |
|
# |
|
# Korman is distributed in the hope that it will be useful, |
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
# GNU General Public License for more details. |
|
# |
|
# You should have received a copy of the GNU General Public License |
|
# along with Korman. If not, see <http://www.gnu.org/licenses/>. |
|
|
|
import bpy |
|
import ctypes |
|
import math |
|
import sys |
|
|
|
if sys.platform == "win32": |
|
class _Coord(ctypes.Structure): |
|
_fields_ = [("x", ctypes.c_short), |
|
("y", ctypes.c_short)] |
|
class _SmallRect(ctypes.Structure): |
|
_fields_ = [("Left", ctypes.c_short), |
|
("Top", ctypes.c_short), |
|
("Right", ctypes.c_short), |
|
("Bottom", ctypes.c_short),] |
|
class _ConsoleScreenBufferInfo(ctypes.Structure): |
|
_fields_ = [("dwSize", _Coord), |
|
("dwCursorPosition", _Coord), |
|
("wAttributes", ctypes.c_ushort), |
|
("srWindow", _SmallRect), |
|
("dwMaximumWindowSize", _Coord)] |
|
|
|
class _ConsoleCursor: |
|
def __init__(self): |
|
self._handle = ctypes.windll.kernel32.GetStdHandle(-11) |
|
self.position = _Coord(0, 0) |
|
|
|
@property |
|
def _screen_buffer_info(self): |
|
info = _ConsoleScreenBufferInfo() |
|
ctypes.windll.kernel32.GetConsoleScreenBufferInfo(self._handle, ctypes.pointer(info)) |
|
return info |
|
|
|
def clear(self): |
|
info = self._screen_buffer_info |
|
curPos = info.dwCursorPosition |
|
num_cols = curPos.y - self.position.y |
|
num_rows = curPos.x - self.position.x |
|
num_chars = (info.dwSize.x * num_cols) + num_rows |
|
if num_chars: |
|
nWrite = ctypes.c_ulong() |
|
empty_char = ctypes.c_char(b' ') |
|
ctypes.windll.kernel32.FillConsoleOutputCharacterA(self._handle, empty_char, |
|
num_chars, self.position, |
|
ctypes.pointer(nWrite)) |
|
|
|
def reset(self): |
|
ctypes.windll.kernel32.SetConsoleCursorPosition(self._handle, self.position) |
|
|
|
def update(self): |
|
info = _ConsoleScreenBufferInfo() |
|
ctypes.windll.kernel32.GetConsoleScreenBufferInfo(self._handle, ctypes.pointer(info)) |
|
self.position = info.dwCursorPosition |
|
else: |
|
class _ConsoleCursor: |
|
def clear(self): |
|
# Only clears the current line, unfortunately. |
|
sys.stdout.write("\x1B[K") |
|
|
|
def reset(self): |
|
# Forcibly clears the line after resetting, just in case more |
|
# than one junk line was printed from somewhere else. |
|
sys.stdout.write("\x1B[u\x1B[K") |
|
|
|
def update(self): |
|
sys.stdout.write("\x1B[s") |
|
|
|
|
|
class ConsoleCursor(_ConsoleCursor): |
|
def __enter__(self): |
|
self.clear() |
|
self.reset() |
|
return self |
|
|
|
def __exit__(self, type, value, traceback): |
|
self.update() |
|
|
|
|
|
class ConsoleToggler: |
|
_instance = None |
|
|
|
def __init__(self, want_console=None): |
|
if want_console is not None: |
|
self._console_wanted = want_console |
|
|
|
def __new__(cls, want_console=None): |
|
if cls._instance is None: |
|
assert want_console is not None |
|
cls._instance = object.__new__(cls) |
|
cls._instance._console_was_visible = cls.is_console_visible() |
|
cls._instance._console_wanted = want_console |
|
cls._instance._context_active = False |
|
cls._instance.keep_console = False |
|
return cls._instance |
|
|
|
def __enter__(self): |
|
if self._context_active: |
|
raise RuntimeError("ConsoleToggler context manager is not reentrant") |
|
self._console_visible = self.is_console_visible() |
|
self._context_active = True |
|
self.activate_console() |
|
return self |
|
|
|
def __exit__(self, type, value, traceback): |
|
if not self._console_was_visible and self._console_wanted: |
|
if self.keep_console: |
|
# Blender thinks the console is currently not visible. However, it actually is. |
|
# So, we will fire off the toggle operator to keep Blender's internal state valid |
|
bpy.ops.wm.console_toggle() |
|
else: |
|
self.hide_console() |
|
self._context_active = False |
|
self.keep_console = False |
|
return False |
|
|
|
def activate_console(self): |
|
if sys.platform == "win32": |
|
hwnd = ctypes.windll.kernel32.GetConsoleWindow() |
|
if self._console_wanted: |
|
ctypes.windll.user32.ShowWindow(hwnd, 1) |
|
if self._console_was_visible or self._console_wanted: |
|
ctypes.windll.user32.BringWindowToTop(hwnd) |
|
|
|
@staticmethod |
|
def hide_console(): |
|
if sys.platform == "win32": |
|
hwnd = ctypes.windll.kernel32.GetConsoleWindow() |
|
ctypes.windll.user32.ShowWindow(hwnd, 0) |
|
|
|
@staticmethod |
|
def is_console_visible(): |
|
if sys.platform == "win32": |
|
hwnd = ctypes.windll.kernel32.GetConsoleWindow() |
|
return bool(ctypes.windll.user32.IsWindowVisible(hwnd)) |
|
|
|
@staticmethod |
|
def is_platform_supported(): |
|
# If you read Blender's source code, GHOST_toggleConsole (the "Toggle System Console" menu |
|
# item) is only implemented on Windows. The majority of our audience is on Windows as well, |
|
# so I honestly don't see this as an issue... |
|
return sys.platform == "win32"
|
|
|