20#include "modules/subsys/posix/VirtualTerminal.h"
21#include "pedigree/kernel/LockGuard.h"
22#include "pedigree/kernel/machine/Machine.h"
23#include "pedigree/kernel/machine/Vga.h"
24#include "pedigree/kernel/process/Process.h"
25#include "pedigree/kernel/process/Scheduler.h"
27#include "modules/subsys/posix/DevFs.h"
28#include "modules/subsys/posix/PosixSubsystem.h"
29#include "modules/system/console/Console.h"
31extern DevFs* g_pDevFs;
33VirtualTerminalManager::VirtualTerminalManager(
DevFsDirectory* parentDir)
36 m_WantedTty(~size_t(0)),
38 m_ParentDir(parentDir),
39 m_bSwitchingLocked(false) {
40 for (
size_t i = 0; i < MAX_VT; ++i) {
41 m_Terminals[i].textio =
nullptr;
42 m_Terminals[i].file =
nullptr;
44 m_Terminals[i].owner = ~size_t(0);
45 m_Terminals[i].ownerThread = ~size_t(0);
48 ByteSet(&m_Terminals[i].mode, 0,
sizeof(m_Terminals[i].mode));
49 m_Terminals[i].mode.mode = VT_AUTO;
50 m_Terminals[i].systemMode = Text;
54VirtualTerminalManager::~VirtualTerminalManager() {
58 for (
size_t i = 1; i < MAX_VT; ++i) {
59 delete m_Terminals[i].textio;
60 m_Terminals[i].textio =
nullptr;
61 m_Terminals[i].file =
nullptr;
63 m_Terminals[i].owner = ~size_t(0);
64 m_Terminals[i].ownerThread = ~size_t(0);
68 m_Terminals[0].textio =
nullptr;
69 m_Terminals[0].file =
nullptr;
73bool VirtualTerminalManager::initialise() {
75 m_pTty =
new TextIO(
String(
"textui"), g_pDevFs->getNextInode(), g_pDevFs, m_ParentDir);
78 m_ParentDir->addEntry(m_pTty->
getName(), m_pTty);
81 "POSIX: no /dev/tty - VirtualTerminalManager failed to "
83 g_pDevFs->revertInode();
92 m_ParentDir->addEntry(pTty1->
getName(), pTty1);
94 m_Terminals[0].textio = m_pTty;
95 m_Terminals[0].file = pTty1;
98 for (
size_t i = 1; i < 8; ++i) {
100 ttyname.Format(
"tty%u", i + 1);
102 TextIO* tio =
new TextIO(ttyname, g_pDevFs->getNextInode(), g_pDevFs, m_ParentDir);
105 m_ParentDir->addEntry(ttyname, file);
107 m_Terminals[i].textio = tio;
108 m_Terminals[i].file = file;
116 WARNING(
"POSIX: failed to create " << ttyname);
117 g_pDevFs->revertInode();
125bool VirtualTerminalManager::isTerminal(
size_t n)
const {
126 return n < MAX_VT && m_Terminals[n].textio;
129void VirtualTerminalManager::switchTerminal(
size_t n) {
132 m_WantedTty = ~size_t(0);
133 if (m_Terminals[n].systemMode == Text) {
134 if (Machine::instance().getNumVga()) {
139 if (!sendSignal(n,
true) && m_Terminals[n].mode.mode == VT_PROCESS) {
140 m_Terminals[n].mode.mode = VT_AUTO;
141 m_Terminals[n].systemMode = Text;
142 if (Machine::instance().getNumVga()) {
151 if (!isTerminal(n) || m_bSwitchingLocked) {
154 if (n == m_CurrentTty) {
157 if (m_WantedTty != ~
size_t(0)) {
158 if (sendSignal(m_CurrentTty,
false)) {
161 m_WantedTty = ~size_t(0);
163 if (m_Terminals[m_CurrentTty].mode.mode == VT_PROCESS) {
165 if (sendSignal(m_CurrentTty,
false)) {
169 m_Terminals[m_CurrentTty].mode.mode = VT_AUTO;
170 m_Terminals[m_CurrentTty].systemMode = Text;
178 if (n != m_CurrentTty || m_WantedTty == ~
size_t(0)) {
186 if (perm == Disallowed) {
187 m_WantedTty = ~size_t(0);
189 switchTerminal(m_WantedTty);
194bool VirtualTerminalManager::acknowledgeAcquire(
size_t n) {
196 if (n != m_CurrentTty || m_Terminals[n].mode.mode != VT_PROCESS) {
208 for (
size_t i = 0; i < MAX_VT; ++i) {
209 if (m_Terminals[i].textio ==
nullptr) {
210 NOTICE(
"VirtualTerminalManager: opening inactive VT #" << i);
213 ttyname.Format(
"tty%u", i + 1);
215 TextIO* tio =
new TextIO(ttyname, g_pDevFs->getNextInode(), g_pDevFs, m_ParentDir);
218 m_ParentDir->addEntry(tio->
getName(), file);
220 m_Terminals[i].textio = tio;
221 m_Terminals[i].file = file;
231 WARNING(
"POSIX: failed to create " << ttyname);
232 g_pDevFs->revertInode();
243 m_bSwitchingLocked = locked;
246size_t VirtualTerminalManager::getCurrentTerminalNumber()
const {
250TextIO* VirtualTerminalManager::getCurrentTerminal()
const {
251 return m_Terminals[m_CurrentTty].textio;
254File* VirtualTerminalManager::getCurrentTerminalFile()
const {
255 return m_Terminals[m_CurrentTty].file;
260 return m_Terminals[n].mode;
263bool VirtualTerminalManager::setTerminalMode(
size_t n,
struct vt_mode mode) {
265 if (!isTerminal(n) || (mode.mode != VT_AUTO && mode.mode != VT_PROCESS) ||
266 (mode.mode == VT_PROCESS &&
267 (mode.relsig <= 0 || mode.acqsig <= 0 ||
268 static_cast<size_t>(mode.relsig) > PosixSubsystem::MaximumSupportedSignal ||
269 static_cast<size_t>(mode.acqsig) > PosixSubsystem::MaximumSupportedSignal))) {
272 m_Terminals[n].mode = mode;
275 m_Terminals[n].owner = mode.mode == VT_PROCESS ? thread->
getParent()->
getId() : ~size_t(0);
276 m_Terminals[n].ownerThread = mode.mode == VT_PROCESS ? thread->
getId() : ~size_t(0);
278 if (n == m_CurrentTty && mode.mode == VT_AUTO && m_WantedTty != ~
size_t(0)) {
279 switchTerminal(m_WantedTty);
288 state.v_active = m_CurrentTty + 1;
291 for (
size_t i = 0; i < 15; ++i) {
292 if (m_Terminals[i].textio !=
nullptr) {
293 state.v_state |= (1 << (i + 1));
298 NOTICE(
" -> active = " << state.v_active);
299 NOTICE(
" -> state = " <<
Hex << state.v_state);
304void VirtualTerminalManager::setSystemMode(
size_t n, SystemMode mode) {
306 m_Terminals[n].systemMode = mode;
307 if (n != m_CurrentTty) {
310 if (mode == Graphics) {
313 if (Machine::instance().getNumVga()) {
320VirtualTerminalManager::SystemMode VirtualTerminalManager::getSystemMode(
size_t n)
const {
322 return m_Terminals[n].systemMode;
325void VirtualTerminalManager::setInputMode(
size_t n, TextIO::InputMode newMode) {
326 if (!m_Terminals[n].textio) {
327 NOTICE(
"VirtualTerminalManager: can't set mode of VT #" << n <<
" as it is inactive");
331 m_Terminals[n].textio->
setMode(newMode);
334TextIO::InputMode VirtualTerminalManager::getInputMode(
size_t n)
const {
335 if (!m_Terminals[n].textio) {
336 NOTICE(
"VirtualTerminalManager: can't get mode of VT #" << n <<
" as it is inactive");
337 return TextIO::Standard;
340 return m_Terminals[n].textio->
getMode();
343bool VirtualTerminalManager::sendSignal(
size_t n,
bool acq) {
344 auto& terminal = m_Terminals[n];
345 if (terminal.mode.mode != VT_PROCESS) {
351 process->getState() >= Process::Terminating) {
357 !target->acceptingEvents()) {
362 subsystem->
sendSignal(target.get(), acq ? terminal.mode.acqsig : terminal.mode.relsig,
false,
virtual Vga * getVga(size_t n)=0
virtual void sendSignal(Thread *pThread, int signal, bool yield=true, bool processDirected=false)
MUST_USE_RESULT bool acquireThreadById(ThreadLease &lease, size_t id)
static ProcessorInformation & information()
static Scheduler & instance()
bool initialise(bool bClear=true)
InputMode getMode() const
void writeStr(const char *s, size_t len)
void setMode(InputMode mode)
Process * getParent() const
virtual bool setLargestTextMode()=0
void lockSwitching(bool locked)
bool reportPermission(size_t n, SwitchPermission perm)
bool activate(size_t n)
Starts the process of activating the given tty.