Skip to content

Commit

Permalink
Initial work on terminal tab based on xcode.js
Browse files Browse the repository at this point in the history
  • Loading branch information
gtritchie committed Nov 8, 2016
1 parent fc34d66 commit b6a4aa3
Show file tree
Hide file tree
Showing 39 changed files with 10,935 additions and 78 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ src/gwt/src/org/rstudio/core/client/jsonrpc/json2.min.js
src/gwt/www/js/json2.min.js
src/gwt/tools/ace/
src/gwt/tools/pdfjs/
src/gwt/tools/xterm.js/
src/gwt/.ant-targets-build.xml
/src/gwt/.gwt/.gwt-log
.Rproj.user
Expand Down
16 changes: 15 additions & 1 deletion src/cpp/core/include/core/system/Process.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ namespace system {
//
//

// Struct for speicfying pseudoterminal options
// Struct for specifying pseudoterminal options
struct Pseudoterminal
{
Pseudoterminal(int cols, int rows)
Expand All @@ -59,14 +59,19 @@ struct ProcessOptions
ProcessOptions()
#ifdef _WIN32
: terminateChildren(false),
smartTerminal(false)
detachProcess(false),
createNewConsole(false),
breakawayFromJob(false),
redirectStdErrToStdOut(false)
#else
: terminateChildren(false),
smartTerminal(false),
detachSession(false),
cols(80),
rows(25),
redirectStdErrToStdOut(false)

#endif
{
}
Expand All @@ -86,12 +91,21 @@ struct ProcessOptions
// CreateJobObject/CREATE_BREAKAWAY_FROM_JOB to get the same effect
bool terminateChildren;

// Use xterm as terminal type and disable canonical line-by-line
// I/O processing
bool smartTerminal;

#ifndef _WIN32
// Calls ::setsid after fork for POSIX (no effect on Windows)
bool detachSession;

// attach the child process to pseudoterminal pipes
boost::optional<Pseudoterminal> pseudoterminal;

// pseudoterminal size
int cols;
int rows;

#endif

#ifdef _WIN32
Expand Down
7 changes: 7 additions & 0 deletions src/cpp/core/system/PosixChildProcess.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,13 @@ Error ChildProcess::run()
::cfmakeraw(&termp);
termp.c_lflag |= ISIG;

// for smart terminals we need to echo back the user input
if (options_.smartTerminal)
{
termp.c_lflag |= ECHO;
termp.c_oflag |= OPOST|ONLCR;
}

// set attribs
safePosixCall<int>(
boost::bind(::tcsetattr, STDIN_FILENO, TCSANOW, &termp),
Expand Down
66 changes: 59 additions & 7 deletions src/cpp/session/SessionConsoleProcess.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ const int kDefaultMaxOutputLines = 500;
ConsoleProcess::ConsoleProcess()
: dialog_(false), showOnOutput_(false), interactionMode_(InteractionNever),
maxOutputLines_(kDefaultMaxOutputLines), started_(true),
interrupt_(false), outputBuffer_(OUTPUT_BUFFER_SIZE)
interrupt_(false), resize_(false), newCols_(-1), newRows_(-1),
outputBuffer_(OUTPUT_BUFFER_SIZE)
{
regexInit();

Expand All @@ -71,7 +72,7 @@ ConsoleProcess::ConsoleProcess(const std::string& command,
: command_(command), options_(options), caption_(caption), dialog_(dialog),
showOnOutput_(false),
interactionMode_(interactionMode), maxOutputLines_(maxOutputLines),
started_(false), interrupt_(false),
started_(false), interrupt_(false), resize_(false), newCols_(-1), newRows_(-1),
outputBuffer_(OUTPUT_BUFFER_SIZE)
{
commonInit();
Expand All @@ -87,7 +88,7 @@ ConsoleProcess::ConsoleProcess(const std::string& program,
: program_(program), args_(args), options_(options), caption_(caption), dialog_(dialog),
showOnOutput_(false),
interactionMode_(interactionMode), maxOutputLines_(maxOutputLines),
started_(false), interrupt_(false),
started_(false), interrupt_(false), resize_(false), newCols_(-1), newRows_(-1),
outputBuffer_(OUTPUT_BUFFER_SIZE)
{
commonInit();
Expand Down Expand Up @@ -136,17 +137,20 @@ void ConsoleProcess::commonInit()
}
#else
// request a pseudoterminal if this is an interactive console process
options_.pseudoterminal = core::system::Pseudoterminal(80, 1);
options_.pseudoterminal = core::system::Pseudoterminal(options_.cols,
options_.rows);

// define TERM to dumb (but first make sure we have an environment
// define TERM (but first make sure we have an environment
// block to modify)
if (!options_.environment)
{
core::system::Options childEnv;
core::system::environment(&childEnv);
options_.environment = childEnv;
}
core::system::setenv(&(options_.environment.get()), "TERM", "dumb");

core::system::setenv(&(options_.environment.get()), "TERM",
options_.smartTerminal ? "xterm" : "dumb");
#endif
}

Expand Down Expand Up @@ -206,6 +210,13 @@ void ConsoleProcess::interrupt()
interrupt_ = true;
}

void ConsoleProcess::resize(int cols, int rows)
{
newCols_ = cols;
newRows_ = rows;
resize_ = true;
}

bool ConsoleProcess::onContinue(core::system::ProcessOperations& ops)
{
// full stop interrupt if requested
Expand Down Expand Up @@ -248,6 +259,12 @@ bool ConsoleProcess::onContinue(core::system::ProcessOperations& ops)
}
}

if (resize_)
{
resize_ = false;
ops.ptySetSize(newCols_, newRows_);
}

// continue
return true;
}
Expand Down Expand Up @@ -279,6 +296,14 @@ void ConsoleProcess::enqueOutputEvent(const std::string &output, bool error)
void ConsoleProcess::onStdout(core::system::ProcessOperations& ops,
const std::string& output)
{
if (options_.smartTerminal)
{
// TODO: consider extracting out behaviors that vary between smart
// and dumb terminal handlers
enqueOutputEvent(output, false);
return;
}

// convert line endings to posix
std::string posixOutput = output;
string_utils::convertLineEndings(&posixOutput,
Expand Down Expand Up @@ -536,6 +561,32 @@ Error procWriteStdin(const json::JsonRpcRequest& request,
}
}

Error procSetSize(const json::JsonRpcRequest& request,
json::JsonRpcResponse* pResponse)
{
std::string handle;
int cols, rows;
Error error = json::readParams(request.params,
&handle,
&cols,
&rows);
if (error)
return error;

ProcTable::const_iterator pos = s_procs.find(handle);
if (pos != s_procs.end())
{
pos->second->resize(cols, rows);
return Success();

}
else
{
return systemError(boost::system::errc::invalid_argument,
ERROR_LOCATION);
}
}

boost::shared_ptr<ConsoleProcess> ConsoleProcess::create(
const std::string& command,
core::system::ProcessOptions options,
Expand Down Expand Up @@ -756,7 +807,8 @@ Error initialize()
(bind(registerRpcMethod, "process_start", procStart))
(bind(registerRpcMethod, "process_interrupt", procInterrupt))
(bind(registerRpcMethod, "process_reap", procReap))
(bind(registerRpcMethod, "process_write_stdin", procWriteStdin));
(bind(registerRpcMethod, "process_write_stdin", procWriteStdin))
(bind(registerRpcMethod, "process_set_size", procSetSize));

return initBlock.execute();
}
Expand Down
6 changes: 6 additions & 0 deletions src/cpp/session/include/session/SessionConsoleProcess.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ class ConsoleProcess : boost::noncopyable,
core::Error start();
void enqueInput(const Input& input);
void interrupt();
void resize(int cols, int rows);

void setShowOnOutput(bool showOnOutput) { showOnOutput_ = showOnOutput; }

Expand Down Expand Up @@ -173,6 +174,11 @@ class ConsoleProcess : boost::noncopyable,

// Whether the process should be stopped
bool interrupt_;

// Whether the tty should be notified of a resize
bool resize_;
int newCols_;
int newRows_;

// Pending input (writes or ptyInterrupts)
std::queue<Input> inputQueue_;
Expand Down
36 changes: 29 additions & 7 deletions src/cpp/session/modules/SessionWorkbench.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -858,24 +858,43 @@ Error startShellDialog(const json::JsonRpcRequest& request,
using namespace session::module_context;
using namespace session::console_process;

// TERM setting, must correspond to one of the values in the
// client-side enum TerminalType. For now we treat XTERM as a
// "smart terminal" and anything else as DUMB (RStudio 1.0 behavior).
std::string term;

// initial size of the pseudo-terminal
int cols, rows;

Error error = json::readParams(request.params,
&term,
&cols,
&rows);
if (error)
return error;

bool smartTerm = !term.compare("XTERM");

// configure environment for shell
core::system::Options shellEnv;
core::system::environment(&shellEnv);

// set dumb terminal
core::system::setenv(&shellEnv, "TERM", "dumb");
// set terminal
core::system::setenv(&shellEnv, "TERM", smartTerm ? "xterm" : "dumb");

// set prompt
std::string path = module_context::createAliasedPath(
module_context::safeCurrentPath());
std::string prompt = (path.length() > 30) ? "\\W$ " : "\\w$ ";
core::system::setenv(&shellEnv, "PS1", prompt);

// disable screen oriented facillites
core::system::unsetenv(&shellEnv, "EDITOR");
core::system::unsetenv(&shellEnv, "VISUAL");
core::system::setenv(&shellEnv, "PAGER", "/bin/cat");

// disable screen oriented facillites
if (!smartTerm)
{
core::system::unsetenv(&shellEnv, "EDITOR");
core::system::unsetenv(&shellEnv, "VISUAL");
core::system::setenv(&shellEnv, "PAGER", "/bin/cat");
}
core::system::setenv(&shellEnv, "GIT_EDITOR", s_editFileCommand);
core::system::setenv(&shellEnv, "SVN_EDITOR", s_editFileCommand);

Expand All @@ -886,6 +905,9 @@ Error startShellDialog(const json::JsonRpcRequest& request,
core::system::ProcessOptions options;
options.workingDir = module_context::shellWorkingDirectory();
options.environment = shellEnv;
options.smartTerminal = smartTerm;
options.cols = cols;
options.rows = rows;

// configure bash command
core::shell_utils::ShellCommand bashCommand("/usr/bin/env");
Expand Down
1 change: 1 addition & 0 deletions src/gwt/src/org/rstudio/core/client/ElementIds.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,5 @@ public static String getElementId(String id)
public final static String POPUP_COMPLETIONS = "popup_completions";
public final static String SHELL_WIDGET = "shell_widget";
public final static String SOURCE_TEXT_EDITOR = "source_text_editor";
public final static String XTERM_WIDGET = "xterm_widget";
}
Loading

0 comments on commit b6a4aa3

Please sign in to comment.