Pty.start constructor

Pty.start(
  1. String executable, {
  2. List<String> arguments = const [],
  3. String? workingDirectory,
  4. Map<String, String>? environment,
  5. String? terminalProgramVersion,
  6. int rows = 25,
  7. int columns = 80,
  8. bool ackRead = false,
})

Spawns a process in a pseudo-terminal. The arguments have the same meaning as in Process.start. ackRead indicates if the pty should wait for a call to Pty.ackRead before sending the next data.

Implementation

Pty.start(
  this.executable, {
  this.arguments = const [],
  String? workingDirectory,
  Map<String, String>? environment,
  String? terminalProgramVersion,
  int rows = 25,
  int columns = 80,
  bool ackRead = false,
}) {
  validatePtyStartOptions(
    executable: executable,
    arguments: arguments,
    workingDirectory: workingDirectory,
    environment: environment,
    terminalProgramVersion: terminalProgramVersion,
    rows: rows,
    columns: columns,
  );
  _ensureInitialized();

  final caseInsensitiveEnvironment = Platform.isWindows;
  final effectiveEnv = buildPtyEnvironment(
    Platform.environment,
    environment,
    caseInsensitive: caseInsensitiveEnvironment,
    terminalProgramVersion: terminalProgramVersion,
  );
  final environmentEntries = orderPtyEnvironment(
    effectiveEnv,
    caseInsensitive: caseInsensitiveEnvironment,
  );

  // build argv
  final argv = calloc<Pointer<Utf8>>(arguments.length + 2);
  argv.value = executable.toNativeUtf8();
  for (var i = 0; i < arguments.length; i++) {
    (argv + i + 1).value = arguments[i].toNativeUtf8();
  }
  (argv + arguments.length + 1).value = nullptr;

  //build env
  final envp = calloc<Pointer<Utf8>>(environmentEntries.length + 1);
  var environmentIndex = 0;
  for (final entry in environmentEntries) {
    (envp + environmentIndex).value =
        '${entry.key}=${entry.value}'.toNativeUtf8();
    environmentIndex++;
  }
  (envp + environmentEntries.length).value = nullptr;

  final options = calloc<PtyOptions>();
  options.ref.rows = rows;
  options.ref.cols = columns;
  options.ref.executable = executable.toNativeUtf8().cast();
  options.ref.arguments = argv.cast();
  options.ref.environment = envp.cast();
  options.ref.stdout_port = _stdoutPort.sendPort.nativePort;
  options.ref.exit_port = _exitPort.sendPort.nativePort;
  options.ref.output_done_port = _outputDonePort.sendPort.nativePort;
  options.ref.ackRead = ackRead;

  if (workingDirectory != null) {
    options.ref.working_directory = workingDirectory.toNativeUtf8().cast();
  } else {
    options.ref.working_directory = nullptr;
  }

  _handle = _bindings.pty_create(options);

  malloc.free(options.ref.executable);
  if (options.ref.working_directory != nullptr) {
    malloc.free(options.ref.working_directory);
  }
  for (var i = 0; i < arguments.length + 1; i++) {
    malloc.free((argv + i).value);
  }
  calloc.free(argv);
  for (var i = 0; i < environmentEntries.length; i++) {
    malloc.free((envp + i).value);
  }
  calloc.free(envp);
  calloc.free(options);

  if (_handle == nullptr) {
    final error = _getPtyError();
    _stdoutPort.close();
    _exitPort.close();
    _outputDonePort.close();
    throw StateError('Failed to create PTY: $error');
  }

  _exitPort.listen(_onNativeExit);
  _outputDonePort.listen(_onOutputDone);
}