* consoled no longer talks to its own keyboard driver, but uses the new debugger
feature of the usual keyboard drivers (it currently only uses the first one always, though, instead of all of them). * Also, it now loads your current keymap from disk, so you don't need to adapt to a different keymap anymore. However, right now, it does not have a built-in keymap, so this won't work if there is no Key_map file. This will be done later with some more cleanup. * It now prints error messages to the syslog/serial as well over debug_printf(). * Removed the BeOS R5 input server mode support. * Some more cleanup. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@36260 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -1,6 +1,25 @@
|
||||
SubDir HAIKU_TOP src tests system consoled ;
|
||||
|
||||
UsePrivateHeaders input ;
|
||||
UsePrivateHeaders kernel ;
|
||||
UseHeaders [ FDirName $(HAIKU_TOP) src apps terminal ] ;
|
||||
UseHeaders [ FDirName $(HAIKU_COMMON_DEBUG_OBJECT_DIR) servers input ] ;
|
||||
|
||||
Application consoled : consoled.cpp : $(TARGET_LIBSUPC++) ;
|
||||
SEARCH_SOURCE += [ FDirName $(HAIKU_TOP) src add-ons input_server devices
|
||||
keyboard ] ;
|
||||
SEARCH_SOURCE += [ FDirName $(HAIKU_TOP) src servers input ] ;
|
||||
|
||||
{
|
||||
local defines = [ FDefines CONSOLED ] ;
|
||||
SubDirCcFlags $(defines) -Wall -Wno-multichar ;
|
||||
SubDirC++Flags $(defines) -Wall -Wno-multichar ;
|
||||
}
|
||||
|
||||
Application consoled :
|
||||
consoled.cpp
|
||||
|
||||
Keymap.cpp
|
||||
: $(TARGET_LIBSUPC++)
|
||||
;
|
||||
|
||||
Includes [ FGristFiles consoled.cpp ] : <src!servers!input>SystemKeymap.h ;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2004-2007, Haiku. All rights reserved.
|
||||
* Copyright 2004-2010, Haiku. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Copyright 2002, Travis Geiselbrecht. All rights reserved.
|
||||
@@ -7,9 +7,6 @@
|
||||
*/
|
||||
|
||||
|
||||
#include <image.h>
|
||||
#include <OS.h>
|
||||
|
||||
#include <ctype.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
@@ -19,159 +16,239 @@
|
||||
#include <termios.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <image.h>
|
||||
#include <InterfaceDefs.h>
|
||||
#include <OS.h>
|
||||
|
||||
//#define USE_INPUT_SERVER
|
||||
// define this if consoled should use the input_server to
|
||||
// get to its keyboard input
|
||||
// be sure to define USE_R5_STYLE_COMM in InputServer.h when USE_INPUT_SERVER is defined
|
||||
#include <kb_mouse_driver.h>
|
||||
|
||||
#ifdef USE_INPUT_SERVER
|
||||
# include <Message.h>
|
||||
# include <AppDefs.h>
|
||||
# include <InterfaceDefs.h>
|
||||
# include <TypeConstants.h>
|
||||
|
||||
# include "VTkeymap.h"
|
||||
#endif
|
||||
#include "ATKeymap.h"
|
||||
#include "SystemKeymap.h"
|
||||
#include "Keymap.h"
|
||||
|
||||
|
||||
struct console {
|
||||
int console_fd;
|
||||
int keyboard_fd;
|
||||
int tty_master_fd;
|
||||
int tty_slave_fd;
|
||||
int tty_num;
|
||||
thread_id keyboard_reader;
|
||||
thread_id console_writer;
|
||||
int console_fd;
|
||||
int keyboard_fd;
|
||||
bool is_at;
|
||||
|
||||
int tty_master_fd;
|
||||
int tty_slave_fd;
|
||||
int tty_num;
|
||||
|
||||
thread_id keyboard_reader;
|
||||
thread_id console_writer;
|
||||
};
|
||||
|
||||
|
||||
struct console gConsole;
|
||||
|
||||
|
||||
static int32
|
||||
keyboard_reader(void *arg)
|
||||
void
|
||||
error(const char* message, ...)
|
||||
{
|
||||
struct console *con = (struct console *)arg;
|
||||
char buffer[2048];
|
||||
|
||||
#ifdef USE_INPUT_SERVER
|
||||
area_id appArea;
|
||||
void *cursorAddr;
|
||||
sem_id cursorSem = 0; // not initialized?
|
||||
thread_id isThread;
|
||||
port_id isAsPort;
|
||||
port_id isPort;
|
||||
|
||||
appArea = create_area("isCursor", &cursorAddr, B_ANY_ADDRESS, B_PAGE_SIZE, B_FULL_LOCK, B_READ_AREA);
|
||||
|
||||
int32 arg_c = 1;
|
||||
char **arg_v = (char **)malloc(sizeof(char *) * (arg_c + 1));
|
||||
arg_v[0] = strdup("/system/servers/input_server");
|
||||
arg_v[1] = NULL;
|
||||
isThread = load_image(arg_c, (const char**)arg_v, (const char **)environ);
|
||||
va_list args;
|
||||
va_start(args, message);
|
||||
|
||||
free(arg_v[0]);
|
||||
vsnprintf(buffer, sizeof(buffer), message, args);
|
||||
|
||||
int32 tmpbuf[2] = {cursorSem, appArea};
|
||||
int32 code = 0;
|
||||
send_data(isThread, code, (void *)tmpbuf, sizeof(tmpbuf));
|
||||
va_end(args);
|
||||
|
||||
resume_thread(isThread);
|
||||
setpgid(isThread, 0);
|
||||
// put it out on stderr as well as to serial/syslog
|
||||
fputs(buffer, stderr);
|
||||
debug_printf("%s", buffer);
|
||||
}
|
||||
|
||||
thread_id sender;
|
||||
code = receive_data(&sender, (void *)tmpbuf, sizeof(tmpbuf));
|
||||
isAsPort = tmpbuf[0];
|
||||
isPort = tmpbuf[1];
|
||||
|
||||
ssize_t length;
|
||||
status_t err;
|
||||
const char *string;
|
||||
|
||||
void
|
||||
update_leds(int fd, uint32 modifiers)
|
||||
{
|
||||
char lockIO[3] = {0, 0, 0};
|
||||
|
||||
if ((modifiers & B_NUM_LOCK) != 0)
|
||||
lockIO[0] = 1;
|
||||
if ((modifiers & B_CAPS_LOCK) != 0)
|
||||
lockIO[1] = 1;
|
||||
if ((modifiers & B_SCROLL_LOCK) != 0)
|
||||
lockIO[2] = 1;
|
||||
|
||||
ioctl(fd, KB_SET_LEDS, &lockIO);
|
||||
}
|
||||
|
||||
|
||||
static int32
|
||||
keyboard_reader(void* arg)
|
||||
{
|
||||
struct console* con = (struct console*)arg;
|
||||
uint8 activeDeadKey = 0;
|
||||
uint32 modifiers = 0;
|
||||
|
||||
Keymap keymap;
|
||||
keymap.LoadCurrent();
|
||||
|
||||
for (;;) {
|
||||
length = port_buffer_size(isAsPort);
|
||||
char buffer[length];
|
||||
err = read_port(isAsPort, &code, buffer, length);
|
||||
if(err != length) {
|
||||
if (err > 0)
|
||||
fprintf(stderr, "consoled: failed to read full packet (read %lu of %lu)\n", err, length);
|
||||
else
|
||||
fprintf(stderr, "consoled: read_port error: (0x%lx) %s\n", err, strerror(err));
|
||||
continue;
|
||||
}
|
||||
|
||||
BMessage event;
|
||||
if ((err = event.Unflatten(buffer)) < 0) {
|
||||
fprintf(stderr, "consoled: Unflatten() error: (0x%lx) %s\n", err, strerror(err));
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((event.what != B_KEY_DOWN)
|
||||
|| (event.FindString("bytes", &string) != B_OK))
|
||||
continue;
|
||||
|
||||
length = strlen(string);
|
||||
if (length == 1)
|
||||
switch (*string) {
|
||||
case B_LEFT_ARROW:
|
||||
write(con->tty_master_fd, LEFT_ARROW_KEY_CODE, sizeof(LEFT_ARROW_KEY_CODE));
|
||||
break;
|
||||
case B_RIGHT_ARROW:
|
||||
write(con->tty_master_fd, RIGHT_ARROW_KEY_CODE, sizeof(RIGHT_ARROW_KEY_CODE));
|
||||
break;
|
||||
case B_UP_ARROW:
|
||||
write(con->tty_master_fd, UP_ARROW_KEY_CODE, sizeof(UP_ARROW_KEY_CODE));
|
||||
break;
|
||||
case B_DOWN_ARROW:
|
||||
write(con->tty_master_fd, DOWN_ARROW_KEY_CODE, sizeof(DOWN_ARROW_KEY_CODE));
|
||||
break;
|
||||
default:
|
||||
write(con->tty_master_fd, string, length);
|
||||
}
|
||||
else
|
||||
write(con->tty_master_fd, string, length);
|
||||
}
|
||||
#else // USE_INPUT_SERVER
|
||||
char buffer[1024];
|
||||
|
||||
for (;;) {
|
||||
ssize_t length = read(con->keyboard_fd, buffer, sizeof(buffer));
|
||||
if (length < 0)
|
||||
char buffer[16];
|
||||
if (ioctl(con->keyboard_fd, KB_READ, &buffer) != 0)
|
||||
break;
|
||||
|
||||
write(con->tty_master_fd, buffer, length);
|
||||
uint32 keycode = 0;
|
||||
bool isKeyDown = false;
|
||||
bigtime_t timestamp = 0;
|
||||
|
||||
if (con->is_at) {
|
||||
at_kbd_io* atKeyboard = (at_kbd_io*)buffer;
|
||||
if (atKeyboard->scancode > 0)
|
||||
keycode = kATKeycodeMap[atKeyboard->scancode - 1];
|
||||
isKeyDown = atKeyboard->is_keydown;
|
||||
timestamp = atKeyboard->timestamp;
|
||||
} else {
|
||||
raw_key_info* rawKeyInfo= (raw_key_info*)buffer;
|
||||
isKeyDown = rawKeyInfo->is_keydown;
|
||||
timestamp = rawKeyInfo->timestamp;
|
||||
keycode = rawKeyInfo->be_keycode;
|
||||
}
|
||||
|
||||
if (keycode == 0)
|
||||
continue;
|
||||
|
||||
uint32 changedModifiers = keymap.Modifier(keycode);
|
||||
bool isLock = (changedModifiers
|
||||
& (B_CAPS_LOCK | B_NUM_LOCK | B_SCROLL_LOCK)) != 0;
|
||||
if (changedModifiers != 0 && (!isLock || isKeyDown)) {
|
||||
uint32 oldModifiers = modifiers;
|
||||
|
||||
if ((isKeyDown && !isLock)
|
||||
|| (isKeyDown && !(modifiers & changedModifiers)))
|
||||
modifiers |= changedModifiers;
|
||||
else {
|
||||
modifiers &= ~changedModifiers;
|
||||
|
||||
// ensure that we don't clear a combined B_*_KEY when still
|
||||
// one of the individual B_{LEFT|RIGHT}_*_KEY is pressed
|
||||
if (modifiers & (B_LEFT_SHIFT_KEY | B_RIGHT_SHIFT_KEY))
|
||||
modifiers |= B_SHIFT_KEY;
|
||||
if (modifiers & (B_LEFT_COMMAND_KEY | B_RIGHT_COMMAND_KEY))
|
||||
modifiers |= B_COMMAND_KEY;
|
||||
if (modifiers & (B_LEFT_CONTROL_KEY | B_RIGHT_CONTROL_KEY))
|
||||
modifiers |= B_CONTROL_KEY;
|
||||
if (modifiers & (B_LEFT_OPTION_KEY | B_RIGHT_OPTION_KEY))
|
||||
modifiers |= B_OPTION_KEY;
|
||||
}
|
||||
|
||||
if (modifiers != oldModifiers) {
|
||||
if (isLock)
|
||||
update_leds(con->keyboard_fd, modifiers);
|
||||
}
|
||||
}
|
||||
|
||||
uint8 newDeadKey = 0;
|
||||
if (activeDeadKey == 0 || !isKeyDown)
|
||||
newDeadKey = keymap.IsDeadKey(keycode, modifiers);
|
||||
|
||||
char* string = NULL;
|
||||
int32 numBytes = 0;
|
||||
if (newDeadKey == 0 && isKeyDown) {
|
||||
keymap.GetChars(keycode, modifiers, activeDeadKey, &string,
|
||||
&numBytes);
|
||||
if (numBytes > 0)
|
||||
write(con->tty_master_fd, string, numBytes);
|
||||
|
||||
delete[] string;
|
||||
}
|
||||
|
||||
if (newDeadKey == 0) {
|
||||
if (isKeyDown && !modifiers && activeDeadKey != 0) {
|
||||
// a dead key was completed
|
||||
activeDeadKey = 0;
|
||||
}
|
||||
} else if (isKeyDown) {
|
||||
// start of a dead key
|
||||
char* string = NULL;
|
||||
int32 numBytes = 0;
|
||||
keymap.GetChars(keycode, modifiers, 0, &string, &numBytes);
|
||||
|
||||
activeDeadKey = newDeadKey;
|
||||
}
|
||||
}
|
||||
#endif // USE_INPUT_SERVER
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int32
|
||||
console_writer(void *arg)
|
||||
console_writer(void* arg)
|
||||
{
|
||||
char buf[1024];
|
||||
ssize_t len;
|
||||
ssize_t write_len;
|
||||
struct console *con = (struct console *)arg;
|
||||
struct console* con = (struct console*)arg;
|
||||
|
||||
for (;;) {
|
||||
len = read(con->tty_master_fd, buf, sizeof(buf));
|
||||
if (len < 0)
|
||||
char buffer[1024];
|
||||
ssize_t length = read(con->tty_master_fd, buffer, sizeof(buffer));
|
||||
if (length < 0)
|
||||
break;
|
||||
|
||||
write_len = write(con->console_fd, buf, len);
|
||||
write(con->console_fd, buffer, length);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*! Opens the first keyboard driver it finds starting from the given
|
||||
location \a start that supports the debugger extension.
|
||||
\a isAT determines whether this is an AT keyboard (that returns
|
||||
different keycodes) or not.
|
||||
*/
|
||||
static int
|
||||
open_keyboard(const char* start, bool& isAT)
|
||||
{
|
||||
DIR* dir = opendir(start);
|
||||
if (dir == NULL)
|
||||
return -1;
|
||||
|
||||
int fd = -1;
|
||||
|
||||
while (true) {
|
||||
dirent* entry = readdir(dir);
|
||||
if (entry == NULL)
|
||||
break;
|
||||
if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, ".."))
|
||||
continue;
|
||||
|
||||
char path[PATH_MAX];
|
||||
strlcpy(path, start, sizeof(path));
|
||||
strlcat(path, "/", sizeof(path));
|
||||
strlcat(path, entry->d_name, sizeof(path));
|
||||
|
||||
struct stat stat;
|
||||
if (::stat(path, &stat) != 0)
|
||||
continue;
|
||||
|
||||
if (S_ISDIR(stat.st_mode))
|
||||
return open_keyboard(path, isAT);
|
||||
|
||||
// Try to open it as a device
|
||||
fd = open(path, O_RDONLY);
|
||||
if (fd >= 0) {
|
||||
// Turn on debugger mode
|
||||
if (ioctl(fd, KB_SET_DEBUG_READER, NULL, 0) == 0) {
|
||||
isAT = strstr(path, "keyboard/at") != NULL;
|
||||
break;
|
||||
}
|
||||
close(fd);
|
||||
fd = -1;
|
||||
}
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
return fd;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
start_console(struct console *con)
|
||||
start_console(struct console* con)
|
||||
{
|
||||
DIR *dir;
|
||||
|
||||
memset(con, 0, sizeof(struct console));
|
||||
con->console_fd = -1;
|
||||
con->keyboard_fd = -1;
|
||||
@@ -184,30 +261,29 @@ start_console(struct console *con)
|
||||
if (con->console_fd < 0)
|
||||
return -2;
|
||||
|
||||
#ifndef USE_INPUT_SERVER
|
||||
con->keyboard_fd = open("/dev/keyboard", O_RDONLY);
|
||||
con->keyboard_fd = open_keyboard("/dev/input/keyboard", con->is_at);
|
||||
if (con->keyboard_fd < 0)
|
||||
return -3;
|
||||
#endif
|
||||
|
||||
dir = opendir("/dev/pt");
|
||||
DIR* dir = opendir("/dev/pt");
|
||||
if (dir != NULL) {
|
||||
struct dirent *entry;
|
||||
struct dirent* entry;
|
||||
char name[64];
|
||||
|
||||
while ((entry = readdir(dir)) != NULL) {
|
||||
if (entry->d_name[0] == '.')
|
||||
continue;
|
||||
|
||||
sprintf(name, "/dev/pt/%s", entry->d_name);
|
||||
|
||||
snprintf(name, sizeof(name), "/dev/pt/%s", entry->d_name);
|
||||
|
||||
con->tty_master_fd = open(name, O_RDWR);
|
||||
if (con->tty_master_fd >= 0) {
|
||||
sprintf(name, "/dev/tt/%s", entry->d_name);
|
||||
snprintf(name, sizeof(name), "/dev/tt/%s", entry->d_name);
|
||||
|
||||
con->tty_slave_fd = open(name, O_RDWR);
|
||||
if (con->tty_slave_fd < 0) {
|
||||
fprintf(stderr, "Could not open tty %s: %s!\n", name, strerror(errno));
|
||||
error("Could not open tty %s: %s!\n", name,
|
||||
strerror(errno));
|
||||
close(con->tty_master_fd);
|
||||
} else {
|
||||
// set default mode
|
||||
@@ -222,9 +298,11 @@ start_console(struct console *con)
|
||||
tcsetattr(con->tty_slave_fd, TCSANOW, &termios);
|
||||
}
|
||||
|
||||
if (ioctl(con->console_fd, TIOCGWINSZ, &size, sizeof(struct winsize)) == 0) {
|
||||
if (ioctl(con->console_fd, TIOCGWINSZ, &size,
|
||||
sizeof(struct winsize)) == 0) {
|
||||
// we got the window size from the console
|
||||
ioctl(con->tty_slave_fd, TIOCSWINSZ, &size, sizeof(struct winsize));
|
||||
ioctl(con->tty_slave_fd, TIOCSWINSZ, &size,
|
||||
sizeof(struct winsize));
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -237,25 +315,19 @@ start_console(struct console *con)
|
||||
if (con->tty_master_fd < 0 || con->tty_slave_fd < 0)
|
||||
return -4;
|
||||
|
||||
con->keyboard_reader = spawn_thread(&keyboard_reader, "console reader", B_URGENT_DISPLAY_PRIORITY, con);
|
||||
con->keyboard_reader = spawn_thread(&keyboard_reader, "console reader",
|
||||
B_URGENT_DISPLAY_PRIORITY, con);
|
||||
if (con->keyboard_reader < 0)
|
||||
return -7;
|
||||
|
||||
con->console_writer = spawn_thread(&console_writer, "console writer", B_URGENT_DISPLAY_PRIORITY, con);
|
||||
con->console_writer = spawn_thread(&console_writer, "console writer",
|
||||
B_URGENT_DISPLAY_PRIORITY, con);
|
||||
if (con->console_writer < 0)
|
||||
return -8;
|
||||
|
||||
resume_thread(con->keyboard_reader);
|
||||
resume_thread(con->console_writer);
|
||||
setenv("TERM", "beterm", true);
|
||||
|
||||
#ifdef USE_INPUT_SERVER
|
||||
// wait for the input_server loop so that keyboard input is available
|
||||
while (find_thread("_input_server_event_loop_") == NULL) {
|
||||
snooze(100000);
|
||||
// a tenth of a second
|
||||
}
|
||||
#endif
|
||||
setenv("TERM", "xterm", true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -270,10 +342,7 @@ stop_console(struct console* con)
|
||||
|
||||
// close console and keyboard
|
||||
close(con->console_fd);
|
||||
#ifndef USE_INPUT_SERVER
|
||||
close(con->keyboard_fd);
|
||||
// TODO: USE_INPUT_SERVER case.
|
||||
#endif
|
||||
|
||||
// wait for the threads
|
||||
status_t returnCode;
|
||||
@@ -283,46 +352,41 @@ stop_console(struct console* con)
|
||||
|
||||
|
||||
static pid_t
|
||||
start_process(int argc, const char **argv, struct console *con)
|
||||
start_process(int argc, const char** argv, struct console* con)
|
||||
{
|
||||
int saved_stdin, saved_stdout, saved_stderr;
|
||||
pid_t pid;
|
||||
|
||||
saved_stdin = dup(0);
|
||||
saved_stdout = dup(1);
|
||||
saved_stderr = dup(2);
|
||||
int savedInput = dup(0);
|
||||
int savedOutput = dup(1);
|
||||
int savedError = dup(2);
|
||||
|
||||
dup2(con->tty_slave_fd, 0);
|
||||
dup2(con->tty_slave_fd, 1);
|
||||
dup2(con->tty_slave_fd, 2);
|
||||
|
||||
pid = load_image(argc, argv, (const char **)environ);
|
||||
pid_t pid = load_image(argc, argv, (const char**)environ);
|
||||
resume_thread(pid);
|
||||
setpgid(pid, 0);
|
||||
tcsetpgrp(con->tty_slave_fd, pid);
|
||||
|
||||
dup2(saved_stdin, 0);
|
||||
dup2(saved_stdout, 1);
|
||||
dup2(saved_stderr, 2);
|
||||
close(saved_stdin);
|
||||
close(saved_stdout);
|
||||
close(saved_stderr);
|
||||
dup2(savedInput, 0);
|
||||
dup2(savedOutput, 1);
|
||||
dup2(savedError, 2);
|
||||
close(savedInput);
|
||||
close(savedOutput);
|
||||
close(savedError);
|
||||
|
||||
return pid;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
main(int argc, char** argv)
|
||||
{
|
||||
int err;
|
||||
|
||||
// we're a session leader
|
||||
setsid();
|
||||
|
||||
err = start_console(&gConsole);
|
||||
int err = start_console(&gConsole);
|
||||
if (err < 0) {
|
||||
printf("consoled: error %d starting console\n", err);
|
||||
error("consoled: error %d starting console.\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -336,31 +400,28 @@ main(int argc, char **argv)
|
||||
|
||||
// get the command argument vector
|
||||
int commandArgc = argc - 1;
|
||||
const char **commandArgv = new const char*[commandArgc + 1];
|
||||
const char** commandArgv = new const char*[commandArgc + 1];
|
||||
for (int i = 0; i < commandArgc; i++)
|
||||
commandArgv[i] = argv[i + 1];
|
||||
|
||||
commandArgv[commandArgc] = NULL;
|
||||
|
||||
// start the process
|
||||
pid_t process;
|
||||
pid_t process = start_process(commandArgc, commandArgv, &gConsole);
|
||||
|
||||
status_t returnCode;
|
||||
|
||||
process = start_process(commandArgc, commandArgv, &gConsole);
|
||||
|
||||
wait_for_thread(process, &returnCode);
|
||||
|
||||
} else {
|
||||
// no command given: start a shell in an endless loop
|
||||
for (;;) {
|
||||
pid_t shellProcess;
|
||||
status_t returnCode;
|
||||
const char *shellArgv[] = { "/bin/sh", "--login", NULL };
|
||||
const char* shellArgv[] = { "/bin/sh", "--login", NULL };
|
||||
|
||||
shellProcess = start_process(2, shellArgv, &gConsole);
|
||||
|
||||
|
||||
wait_for_thread(shellProcess, &returnCode);
|
||||
|
||||
|
||||
puts("Restart shell");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user