From 43ccb2885210eca8caebae3c0f4ba82d61a4a148 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Mon, 21 Oct 2019 20:17:32 +0200 Subject: HackStudio: Embed a Terminal widget below the text editor This will be very useful for running (and interacting with) programs after we build them. :^) --- DevTools/HackStudio/Makefile | 3 +- DevTools/HackStudio/TerminalWrapper.cpp | 95 +++++++++++++++++++++++++++++++++ DevTools/HackStudio/TerminalWrapper.h | 16 ++++++ DevTools/HackStudio/main.cpp | 10 ++-- 4 files changed, 120 insertions(+), 4 deletions(-) create mode 100644 DevTools/HackStudio/TerminalWrapper.cpp create mode 100644 DevTools/HackStudio/TerminalWrapper.h (limited to 'DevTools') diff --git a/DevTools/HackStudio/Makefile b/DevTools/HackStudio/Makefile index d23b1d60a6..23bcd506eb 100644 --- a/DevTools/HackStudio/Makefile +++ b/DevTools/HackStudio/Makefile @@ -2,6 +2,7 @@ include ../../Makefile.common OBJS = \ Project.o \ + TerminalWrapper.o \ main.o APP = HackStudio @@ -11,7 +12,7 @@ DEFINES += -DUSERLAND all: $(APP) $(APP): $(OBJS) - $(LD) -o $(APP) $(LDFLAGS) $(OBJS) -lgui -ldraw -lcore -lc + $(LD) -o $(APP) $(LDFLAGS) $(OBJS) -lvt -lgui -ldraw -lcore -lc .cpp.o: @echo "CXX $<"; $(CXX) $(CXXFLAGS) -o $@ -c $< diff --git a/DevTools/HackStudio/TerminalWrapper.cpp b/DevTools/HackStudio/TerminalWrapper.cpp new file mode 100644 index 0000000000..d1f20bf8bb --- /dev/null +++ b/DevTools/HackStudio/TerminalWrapper.cpp @@ -0,0 +1,95 @@ +#include "TerminalWrapper.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void run_command(int ptm_fd, const String& command) +{ + pid_t pid = fork(); + if (pid == 0) { + const char* tty_name = ptsname(ptm_fd); + if (!tty_name) { + perror("ptsname"); + exit(1); + } + close(ptm_fd); + int pts_fd = open(tty_name, O_RDWR); + if (pts_fd < 0) { + perror("open"); + exit(1); + } + + // NOTE: It's okay if this fails. + (void)ioctl(0, TIOCNOTTY); + + close(0); + close(1); + close(2); + + int rc = dup2(pts_fd, 0); + if (rc < 0) { + perror("dup2"); + exit(1); + } + rc = dup2(pts_fd, 1); + if (rc < 0) { + perror("dup2"); + exit(1); + } + rc = dup2(pts_fd, 2); + if (rc < 0) { + perror("dup2"); + exit(1); + } + rc = close(pts_fd); + if (rc < 0) { + perror("close"); + exit(1); + } + rc = ioctl(0, TIOCSCTTY); + if (rc < 0) { + perror("ioctl(TIOCSCTTY)"); + exit(1); + } + const char* args[4] = { "/bin/Shell", nullptr, nullptr, nullptr }; + if (!command.is_empty()) { + args[1] = "-c"; + args[2] = command.characters(); + } + const char* envs[] = { "TERM=xterm", "PATH=/bin:/usr/bin:/usr/local/bin", nullptr }; + rc = execve("/bin/Shell", const_cast(args), const_cast(envs)); + if (rc < 0) { + perror("execve"); + exit(1); + } + ASSERT_NOT_REACHED(); + } +} + +TerminalWrapper::TerminalWrapper(GWidget* parent) + : GWidget(parent) +{ + set_layout(make(Orientation::Vertical)); + + int ptm_fd = open("/dev/ptmx", O_RDWR); + if (ptm_fd < 0) { + perror("open(ptmx)"); + ASSERT_NOT_REACHED(); + } + + run_command(ptm_fd, "/bin/Shell"); + + RefPtr config = CConfigFile::get_for_app("Terminal"); + m_terminal_widget = TerminalWidget::construct(ptm_fd, config); + add_child(*m_terminal_widget); +} + +TerminalWrapper::~TerminalWrapper() +{ +} diff --git a/DevTools/HackStudio/TerminalWrapper.h b/DevTools/HackStudio/TerminalWrapper.h new file mode 100644 index 0000000000..c36e421a1f --- /dev/null +++ b/DevTools/HackStudio/TerminalWrapper.h @@ -0,0 +1,16 @@ +#pragma once + +#include + +class TerminalWidget; + +class TerminalWrapper final : public GWidget { + C_OBJECT(TerminalWrapper) +public: + virtual ~TerminalWrapper() override; + +private: + explicit TerminalWrapper(GWidget* parent); + + RefPtr m_terminal_widget; +}; diff --git a/DevTools/HackStudio/main.cpp b/DevTools/HackStudio/main.cpp index 8f14f07a4f..9142021493 100644 --- a/DevTools/HackStudio/main.cpp +++ b/DevTools/HackStudio/main.cpp @@ -1,4 +1,5 @@ #include "Project.h" +#include "TerminalWrapper.h" #include #include #include @@ -39,13 +40,14 @@ int main(int argc, char** argv) auto toolbar = GToolBar::construct(widget); - auto splitter = GSplitter::construct(Orientation::Horizontal, widget); - auto project_list_view = GListView::construct(splitter); + auto outer_splitter = GSplitter::construct(Orientation::Horizontal, widget); + auto project_list_view = GListView::construct(outer_splitter); project_list_view->set_model(project->model()); project_list_view->set_size_policy(SizePolicy::Fixed, SizePolicy::Fill); project_list_view->set_preferred_size(200, 0); - auto text_editor = GTextEditor::construct(GTextEditor::MultiLine, splitter); + auto inner_splitter = GSplitter::construct(Orientation::Vertical, outer_splitter); + auto text_editor = GTextEditor::construct(GTextEditor::MultiLine, inner_splitter); text_editor->set_ruler_visible(true); project_list_view->on_activation = [&](auto& index) { @@ -58,6 +60,8 @@ int main(int argc, char** argv) text_editor->set_text(file->read_all()); }; + auto terminal_wrapper = TerminalWrapper::construct(inner_splitter); + auto statusbar = GStatusBar::construct(widget); text_editor->on_cursor_change = [&] { -- cgit v1.2.3