Merge b8210461a295ff90f3fcb66d70cdbc75c57136b9 into 0fc69a4ca5b359504bd017acecda7843a387aa78
This commit is contained in:
commit
00385188b9
@ -31,7 +31,8 @@ libkmscon_core_la_SOURCES = \
|
||||
src/input_xkb.c src/input_xkb.h \
|
||||
external/imKStoUCS.c external\imKStoUCS.h \
|
||||
src/vte.c src/vte.h \
|
||||
src/terminal.c src/terminal.h
|
||||
src/terminal.c src/terminal.h \
|
||||
src/pty.c src/pty.h
|
||||
|
||||
if USE_PANGO
|
||||
libkmscon_core_la_SOURCES += \
|
||||
|
421
src/pty.c
Normal file
421
src/pty.c
Normal file
@ -0,0 +1,421 @@
|
||||
/*
|
||||
* kmscon - Pseudo Terminal Handling
|
||||
*
|
||||
* Copyright (c) 2012 Ran Benita <ran234@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files
|
||||
* (the "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/* for pty functions */
|
||||
#define _XOPEN_SOURCE 700
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <paths.h>
|
||||
#include <pty.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <termios.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "log.h"
|
||||
#include "pty.h"
|
||||
|
||||
struct kmscon_pty {
|
||||
unsigned long ref;
|
||||
struct kmscon_eloop *eloop;
|
||||
|
||||
int fd;
|
||||
struct kmscon_fd *efd;
|
||||
|
||||
kmscon_pty_output_cb output_cb;
|
||||
void *output_data;
|
||||
|
||||
kmscon_pty_closed_cb closed_cb;
|
||||
void *closed_data;
|
||||
};
|
||||
|
||||
int kmscon_pty_new(struct kmscon_pty **out,
|
||||
kmscon_pty_output_cb output_cb, void *data)
|
||||
{
|
||||
struct kmscon_pty *pty;
|
||||
|
||||
if (!out)
|
||||
return -EINVAL;
|
||||
|
||||
log_debug("pty: new pty object\n");
|
||||
|
||||
pty = malloc(sizeof(*pty));
|
||||
if (!pty)
|
||||
return -ENOMEM;
|
||||
|
||||
memset(pty, 0, sizeof(*pty));
|
||||
pty->fd = -1;
|
||||
pty->ref = 1;
|
||||
|
||||
pty->output_cb = output_cb;
|
||||
pty->output_data = data;
|
||||
*out = pty;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void kmscon_pty_ref(struct kmscon_pty *pty)
|
||||
{
|
||||
if (!pty)
|
||||
return;
|
||||
|
||||
pty->ref++;
|
||||
}
|
||||
|
||||
void kmscon_pty_unref(struct kmscon_pty *pty)
|
||||
{
|
||||
if (!pty || !pty->ref)
|
||||
return;
|
||||
|
||||
if (--pty->ref)
|
||||
return;
|
||||
|
||||
kmscon_pty_close(pty);
|
||||
free(pty);
|
||||
log_debug("pty: destroying pty object\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO:
|
||||
* - Decide which terminal we're emulating and set TERM accordingly.
|
||||
* - Decide what to exec here: login, some getty equivalent, a shell...
|
||||
* - Might also need to update some details in utmp wtmp and friends.
|
||||
*/
|
||||
static void __attribute__((noreturn))
|
||||
exec_child(int pty_master)
|
||||
{
|
||||
const char *sh;
|
||||
|
||||
setenv("TERM", "linux", 1);
|
||||
|
||||
sh = getenv("SHELL") ?: _PATH_BSHELL;
|
||||
execlp(sh, sh, "-i", NULL);
|
||||
|
||||
log_err("pty: failed to exec child: %m\n");
|
||||
|
||||
_exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
static int fork_pty_child(int master, struct winsize *ws)
|
||||
{
|
||||
int ret, saved_errno;
|
||||
pid_t pid;
|
||||
const char *slave_name;
|
||||
int slave = -1;
|
||||
|
||||
/* This doesn't actually do anything on linux. */
|
||||
ret = grantpt(master);
|
||||
if (ret < 0) {
|
||||
log_err("pty: grantpt failed: %m");
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
ret = unlockpt(master);
|
||||
if (ret < 0) {
|
||||
log_err("pty: cannot unlock pty: %m");
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
slave_name = ptsname(master);
|
||||
if (!slave_name) {
|
||||
log_err("pty: cannot find slave name: %m");
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
/* This also loses our controlling tty. */
|
||||
pid = setsid();
|
||||
if (pid < 0) {
|
||||
log_err("pty: cannot start a new session: %m");
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
/* And the slave pty becomes our controlling tty. */
|
||||
slave = open(slave_name, O_RDWR | O_CLOEXEC);
|
||||
if (slave < 0) {
|
||||
log_err("pty: cannot open slave: %m");
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
if (ws) {
|
||||
ret = ioctl(slave, TIOCSWINSZ, ws);
|
||||
if (ret)
|
||||
log_warning("pty: cannot set slave window size: %m");
|
||||
}
|
||||
|
||||
if (dup2(slave, STDIN_FILENO) != STDIN_FILENO ||
|
||||
dup2(slave, STDOUT_FILENO) != STDOUT_FILENO ||
|
||||
dup2(slave, STDERR_FILENO) != STDERR_FILENO) {
|
||||
log_err("pty: cannot duplicate slave: %m");
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
close(master);
|
||||
close(slave);
|
||||
return 0;
|
||||
|
||||
err_out:
|
||||
saved_errno = errno;
|
||||
if (slave > 0)
|
||||
close(slave);
|
||||
close(master);
|
||||
return -saved_errno;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is functionally equivalent to forkpty(3). We do it manually to obtain
|
||||
* a little bit more control of the process, and as a bonus avoid linking to
|
||||
* the libutil library in glibc.
|
||||
*/
|
||||
static pid_t fork_pty(int *pty_out, struct winsize *ws)
|
||||
{
|
||||
int ret;
|
||||
pid_t pid;
|
||||
int master;
|
||||
|
||||
master = posix_openpt(O_RDWR | O_NOCTTY | O_CLOEXEC | O_NONBLOCK);
|
||||
if (master < 0) {
|
||||
ret = -errno;
|
||||
log_err("pty: cannot open master: %m");
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
pid = fork();
|
||||
switch (pid) {
|
||||
case -1:
|
||||
log_err("pty: cannot fork: %m");
|
||||
ret = -errno;
|
||||
goto err_master;
|
||||
case 0:
|
||||
ret = fork_pty_child(master, ws);
|
||||
if (ret)
|
||||
goto err_master;
|
||||
*pty_out = -1;
|
||||
return 0;
|
||||
default:
|
||||
*pty_out = master;
|
||||
return pid;
|
||||
}
|
||||
|
||||
err_master:
|
||||
close(master);
|
||||
err_out:
|
||||
*pty_out = -1;
|
||||
errno = -ret;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int pty_spawn(struct kmscon_pty *pty,
|
||||
unsigned short width, unsigned short height)
|
||||
{
|
||||
struct winsize ws;
|
||||
pid_t pid;
|
||||
|
||||
if (pty->fd >= 0)
|
||||
return -EALREADY;
|
||||
|
||||
memset(&ws, 0, sizeof(ws));
|
||||
ws.ws_col = width;
|
||||
ws.ws_row = height;
|
||||
|
||||
pid = fork_pty(&pty->fd, &ws);
|
||||
switch (pid) {
|
||||
case -1:
|
||||
log_err("pty: cannot fork or open pty pair: %m");
|
||||
return -errno;
|
||||
case 0:
|
||||
exec_child(pty->fd);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pty_output(struct kmscon_fd *fd, int mask, void *data)
|
||||
{
|
||||
int ret, nread;
|
||||
ssize_t len;
|
||||
struct kmscon_pty *pty = data;
|
||||
|
||||
if (!pty || pty->fd < 0)
|
||||
return;
|
||||
|
||||
/*
|
||||
* If we get a hangup or an error, but the pty is still readable, we
|
||||
* read what's left and deal with the rest on the next dispatch.
|
||||
*/
|
||||
if (!(mask & KMSCON_READABLE)) {
|
||||
if (mask & KMSCON_ERR)
|
||||
log_warning("pty: error condition happened on pty\n");
|
||||
kmscon_pty_close(pty);
|
||||
return;
|
||||
}
|
||||
|
||||
ret = ioctl(pty->fd, FIONREAD, &nread);
|
||||
if (ret) {
|
||||
log_warning("pty: cannot peek into pty input buffer: %m");
|
||||
return;
|
||||
} else if (nread <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
char u8[nread];
|
||||
len = read(pty->fd, u8, nread);
|
||||
if (len == -1) {
|
||||
if (errno == EWOULDBLOCK)
|
||||
return;
|
||||
/* EIO is hangup, although we should have caught it above. */
|
||||
if (errno != EIO)
|
||||
log_err("pty: cannot read from pty: %m");
|
||||
kmscon_pty_close(pty);
|
||||
return;
|
||||
} else if (len == 0) {
|
||||
kmscon_pty_close(pty);
|
||||
return;
|
||||
}
|
||||
|
||||
if (pty->output_cb)
|
||||
pty->output_cb(pty, u8, len, pty->output_data);
|
||||
}
|
||||
|
||||
static int connect_eloop(struct kmscon_pty *pty, struct kmscon_eloop *eloop)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (pty->eloop)
|
||||
return -EALREADY;
|
||||
|
||||
ret = kmscon_eloop_new_fd(eloop, &pty->efd, pty->fd,
|
||||
KMSCON_READABLE, pty_output, pty);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
kmscon_eloop_ref(eloop);
|
||||
pty->eloop = eloop;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void disconnect_eloop(struct kmscon_pty *pty)
|
||||
{
|
||||
kmscon_eloop_rm_fd(pty->efd);
|
||||
kmscon_eloop_unref(pty->eloop);
|
||||
pty->efd = NULL;
|
||||
pty->eloop = NULL;
|
||||
}
|
||||
|
||||
int kmscon_pty_open(struct kmscon_pty *pty, struct kmscon_eloop *eloop,
|
||||
unsigned short width, unsigned short height,
|
||||
kmscon_pty_closed_cb closed_cb, void *data)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!pty || !eloop)
|
||||
return -EINVAL;
|
||||
|
||||
if (pty->fd >= 0)
|
||||
return -EALREADY;
|
||||
|
||||
ret = pty_spawn(pty, width, height);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = connect_eloop(pty, eloop);
|
||||
if (ret == -EALREADY) {
|
||||
disconnect_eloop(pty);
|
||||
ret = connect_eloop(pty, eloop);
|
||||
}
|
||||
if (ret) {
|
||||
close(pty->fd);
|
||||
pty->fd = -1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
pty->closed_cb = closed_cb;
|
||||
pty->closed_data = data;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void kmscon_pty_close(struct kmscon_pty *pty)
|
||||
{
|
||||
kmscon_pty_closed_cb cb;
|
||||
void *data;
|
||||
|
||||
if (!pty || pty->fd < 0)
|
||||
return;
|
||||
|
||||
disconnect_eloop(pty);
|
||||
|
||||
close(pty->fd);
|
||||
pty->fd = -1;
|
||||
|
||||
cb = pty->closed_cb;
|
||||
data = pty->closed_data;
|
||||
pty->closed_cb = NULL;
|
||||
pty->closed_data = NULL;
|
||||
|
||||
if (cb)
|
||||
cb(pty, data);
|
||||
}
|
||||
|
||||
void kmscon_pty_input(struct kmscon_pty *pty, const char *u8, size_t len)
|
||||
{
|
||||
if (!pty || pty->fd < 0)
|
||||
return;
|
||||
|
||||
/* FIXME: In EWOULDBLOCK we would lose input! Need to buffer. */
|
||||
len = write(pty->fd, u8, len);
|
||||
if (len <= 0) {
|
||||
if (errno != EWOULDBLOCK)
|
||||
kmscon_pty_close(pty);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void kmscon_pty_resize(struct kmscon_pty *pty,
|
||||
unsigned short width, unsigned short height)
|
||||
{
|
||||
int ret;
|
||||
struct winsize ws;
|
||||
|
||||
if (!pty || pty->fd < 0)
|
||||
return;
|
||||
|
||||
memset(&ws, 0, sizeof(ws));
|
||||
ws.ws_col = width;
|
||||
ws.ws_row = height;
|
||||
|
||||
/*
|
||||
* This will send SIGWINCH to the pty slave foreground process group.
|
||||
* We will also get one, but we don't need it.
|
||||
*/
|
||||
ret = ioctl(pty->fd, TIOCSWINSZ, &ws);
|
||||
if (ret) {
|
||||
log_warning("pty: cannot set window size\n");
|
||||
return;
|
||||
}
|
||||
|
||||
log_debug("pty: window size set to %hdx%hd\n", ws.ws_col, ws.ws_row);
|
||||
}
|
72
src/pty.h
Normal file
72
src/pty.h
Normal file
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* kmscon - Pseudo Terminal Handling
|
||||
*
|
||||
* Copyright (c) 2012 Ran Benita <ran234@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files
|
||||
* (the "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The pty object provides an interface for communicating with a child process
|
||||
* over a pseudo terminal. The child is the host, we act as the TTY terminal,
|
||||
* and the kernel is the driver.
|
||||
*
|
||||
* To use this, create a new pty object and open it. You will start receiving
|
||||
* output notifications through the output_cb callback. To communicate with
|
||||
* the other end of the terminal, use the kmscon_pty_input method. All
|
||||
* communication is done using byte streams (presumably UTF-8).
|
||||
*
|
||||
* The pty can be closed voluntarily using the kmson_pty_close method. The
|
||||
* child process can also exit at will; this will be communicated through the
|
||||
* closed_cb callback. The pty object does not wait on the child processes it
|
||||
* spawns; this is the responsibility of the object's user.
|
||||
*/
|
||||
|
||||
#ifndef KMSCON_PTY_H
|
||||
#define KMSCON_PTY_H
|
||||
|
||||
#include "eloop.h"
|
||||
|
||||
struct kmscon_pty;
|
||||
|
||||
typedef void (*kmscon_pty_output_cb)
|
||||
(struct kmscon_pty *pty, char *u8, size_t len, void *data);
|
||||
typedef void (*kmscon_pty_closed_cb) (struct kmscon_pty *pty, void *data);
|
||||
|
||||
int kmscon_pty_new(struct kmscon_pty **out,
|
||||
kmscon_pty_output_cb output_cb, void *data);
|
||||
void kmscon_pty_ref(struct kmscon_pty *pty);
|
||||
void kmscon_pty_unref(struct kmscon_pty *pty);
|
||||
|
||||
int kmscon_pty_open(struct kmscon_pty *pty, struct kmscon_eloop *eloop,
|
||||
unsigned short width, unsigned short height,
|
||||
kmscon_pty_closed_cb closed_cb, void *data);
|
||||
void kmscon_pty_close(struct kmscon_pty *pty);
|
||||
|
||||
void kmscon_pty_input(struct kmscon_pty *pty, const char *u8, size_t len);
|
||||
|
||||
/*
|
||||
* Call this whenever the size of the screen (rows or columns) changes. The
|
||||
* kernel and child process need to be notified.
|
||||
*/
|
||||
void kmscon_pty_resize(struct kmscon_pty *pty,
|
||||
unsigned short width, unsigned short height);
|
||||
|
||||
#endif /* KMSCON_PTY_H */
|
105
src/terminal.c
105
src/terminal.c
@ -39,7 +39,9 @@
|
||||
#include "console.h"
|
||||
#include "eloop.h"
|
||||
#include "font.h"
|
||||
#include "input.h"
|
||||
#include "log.h"
|
||||
#include "pty.h"
|
||||
#include "terminal.h"
|
||||
#include "unicode.h"
|
||||
#include "vte.h"
|
||||
@ -59,6 +61,10 @@ struct kmscon_terminal {
|
||||
struct kmscon_console *console;
|
||||
struct kmscon_idle *redraw;
|
||||
struct kmscon_vte *vte;
|
||||
struct kmscon_pty *pty;
|
||||
|
||||
kmscon_terminal_closed_cb closed_cb;
|
||||
void *closed_data;
|
||||
};
|
||||
|
||||
static void draw_all(struct kmscon_idle *idle, void *data)
|
||||
@ -101,20 +107,17 @@ static void schedule_redraw(struct kmscon_terminal *term)
|
||||
log_warning("terminal: cannot schedule redraw\n");
|
||||
}
|
||||
|
||||
static const char help_text[] =
|
||||
"terminal subsystem - KMS based console test\n"
|
||||
"This is some default text to test the drawing operations.\n\n";
|
||||
|
||||
static void print_help(struct kmscon_terminal *term)
|
||||
static void pty_output(struct kmscon_pty *pty, char *u8, size_t len, void *data)
|
||||
{
|
||||
unsigned int i, len;
|
||||
kmscon_symbol_t ch;
|
||||
size_t i;
|
||||
struct kmscon_terminal *term = data;
|
||||
|
||||
len = sizeof(help_text) - 1;
|
||||
for (i = 0; i < len; ++i) {
|
||||
ch = kmscon_symbol_make(help_text[i]);
|
||||
kmscon_terminal_input(term, ch);
|
||||
}
|
||||
/* FIXME: UTF-8. */
|
||||
for (i=0; i < len; i++)
|
||||
if (u8[i] < 128)
|
||||
kmscon_vte_input(term->vte, u8[i]);
|
||||
|
||||
schedule_redraw(term);
|
||||
}
|
||||
|
||||
int kmscon_terminal_new(struct kmscon_terminal **out,
|
||||
@ -147,11 +150,16 @@ int kmscon_terminal_new(struct kmscon_terminal **out,
|
||||
if (ret)
|
||||
goto err_con;
|
||||
kmscon_vte_bind(term->vte, term->console);
|
||||
print_help(term);
|
||||
|
||||
ret = kmscon_pty_new(&term->pty, pty_output, term);
|
||||
if (ret)
|
||||
goto err_vte;
|
||||
|
||||
*out = term;
|
||||
return 0;
|
||||
|
||||
err_vte:
|
||||
kmscon_vte_unref(term->vte);
|
||||
err_con:
|
||||
kmscon_console_unref(term->console);
|
||||
err_idle:
|
||||
@ -177,16 +185,17 @@ void kmscon_terminal_unref(struct kmscon_terminal *term)
|
||||
if (--term->ref)
|
||||
return;
|
||||
|
||||
term->closed_cb = NULL;
|
||||
kmscon_terminal_close(term);
|
||||
kmscon_terminal_rm_all_outputs(term);
|
||||
kmscon_pty_unref(term->pty);
|
||||
kmscon_vte_unref(term->vte);
|
||||
kmscon_console_unref(term->console);
|
||||
kmscon_terminal_disconnect_eloop(term);
|
||||
free(term);
|
||||
log_debug("terminal: destroying terminal object\n");
|
||||
}
|
||||
|
||||
int kmscon_terminal_connect_eloop(struct kmscon_terminal *term,
|
||||
struct kmscon_eloop *eloop)
|
||||
int connect_eloop(struct kmscon_terminal *term, struct kmscon_eloop *eloop)
|
||||
{
|
||||
if (!term || !eloop)
|
||||
return -EINVAL;
|
||||
@ -200,7 +209,7 @@ int kmscon_terminal_connect_eloop(struct kmscon_terminal *term,
|
||||
return 0;
|
||||
}
|
||||
|
||||
void kmscon_terminal_disconnect_eloop(struct kmscon_terminal *term)
|
||||
void disconnect_eloop(struct kmscon_terminal *term)
|
||||
{
|
||||
if (!term)
|
||||
return;
|
||||
@ -209,6 +218,63 @@ void kmscon_terminal_disconnect_eloop(struct kmscon_terminal *term)
|
||||
term->eloop = NULL;
|
||||
}
|
||||
|
||||
static void pty_closed(struct kmscon_pty *pty, void *data)
|
||||
{
|
||||
struct kmscon_terminal *term = data;
|
||||
kmscon_terminal_close(term);
|
||||
}
|
||||
|
||||
int kmscon_terminal_open(struct kmscon_terminal *term,
|
||||
struct kmscon_eloop *eloop,
|
||||
kmscon_terminal_closed_cb closed_cb, void *data)
|
||||
{
|
||||
int ret;
|
||||
unsigned short width, height;
|
||||
|
||||
if (!term)
|
||||
return -EINVAL;
|
||||
|
||||
ret = connect_eloop(term, eloop);
|
||||
if (ret == -EALREADY) {
|
||||
disconnect_eloop(term);
|
||||
ret = connect_eloop(term, eloop);
|
||||
}
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
width = kmscon_console_get_width(term->console);
|
||||
height = kmscon_console_get_height(term->console);
|
||||
ret = kmscon_pty_open(term->pty, eloop, width, height, pty_closed, term);
|
||||
if (ret) {
|
||||
disconnect_eloop(term);
|
||||
return ret;
|
||||
}
|
||||
|
||||
term->closed_cb = closed_cb;
|
||||
term->closed_data = data;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void kmscon_terminal_close(struct kmscon_terminal *term)
|
||||
{
|
||||
kmscon_terminal_closed_cb cb;
|
||||
void *data;
|
||||
|
||||
if (!term)
|
||||
return;
|
||||
|
||||
cb = term->closed_cb;
|
||||
data = term->closed_data;
|
||||
term->closed_data = NULL;
|
||||
term->closed_cb = NULL;
|
||||
|
||||
disconnect_eloop(term);
|
||||
kmscon_pty_close(term->pty);
|
||||
|
||||
if (cb)
|
||||
cb(term, data);
|
||||
}
|
||||
|
||||
int kmscon_terminal_add_output(struct kmscon_terminal *term,
|
||||
struct kmscon_output *output)
|
||||
{
|
||||
@ -263,6 +329,7 @@ void kmscon_terminal_rm_all_outputs(struct kmscon_terminal *term)
|
||||
|
||||
void kmscon_terminal_input(struct kmscon_terminal *term, kmscon_symbol_t ch)
|
||||
{
|
||||
kmscon_vte_input(term->vte, ch);
|
||||
schedule_redraw(term);
|
||||
/* FIXME: UTF-8. */
|
||||
if (ch < 128)
|
||||
kmscon_pty_input(term->pty, (char *)&ch, 1);
|
||||
}
|
||||
|
@ -41,14 +41,18 @@
|
||||
|
||||
struct kmscon_terminal;
|
||||
|
||||
typedef void (*kmscon_terminal_closed_cb) (struct kmscon_terminal *term,
|
||||
void *data);
|
||||
|
||||
int kmscon_terminal_new(struct kmscon_terminal **out,
|
||||
struct kmscon_font_factory *ff);
|
||||
void kmscon_terminal_ref(struct kmscon_terminal *term);
|
||||
void kmscon_terminal_unref(struct kmscon_terminal *term);
|
||||
|
||||
int kmscon_terminal_connect_eloop(struct kmscon_terminal *term,
|
||||
struct kmscon_eloop *eloop);
|
||||
void kmscon_terminal_disconnect_eloop(struct kmscon_terminal *term);
|
||||
int kmscon_terminal_open(struct kmscon_terminal *term,
|
||||
struct kmscon_eloop *eloop,
|
||||
kmscon_terminal_closed_cb closed_cb, void *data);
|
||||
void kmscon_terminal_close(struct kmscon_terminal *term);
|
||||
|
||||
int kmscon_terminal_add_output(struct kmscon_terminal *term,
|
||||
struct kmscon_output *output);
|
||||
|
@ -36,6 +36,7 @@
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include "eloop.h"
|
||||
#include "input.h"
|
||||
@ -48,6 +49,7 @@ struct app {
|
||||
struct kmscon_eloop *eloop;
|
||||
struct kmscon_signal *sig_term;
|
||||
struct kmscon_signal *sig_int;
|
||||
struct kmscon_signal *sig_chld;
|
||||
struct kmscon_symbol_table *st;
|
||||
struct kmscon_font_factory *ff;
|
||||
struct kmscon_compositor *comp;
|
||||
@ -63,6 +65,63 @@ static void sig_term(struct kmscon_signal *sig, int signum, void *data)
|
||||
terminate = 1;
|
||||
}
|
||||
|
||||
static void sig_chld(struct kmscon_signal *sig, int signum, void *data)
|
||||
{
|
||||
pid_t pid;
|
||||
int status;
|
||||
|
||||
/*
|
||||
* If multiple children exit at the same time, signalfd would put them
|
||||
* all in one event. So we reap in a loop.
|
||||
*/
|
||||
while (1) {
|
||||
pid = waitpid(-1, &status, WNOHANG);
|
||||
if (pid == -1) {
|
||||
if (errno != ECHILD)
|
||||
log_warning("test: cannot wait on child: %m\n");
|
||||
break;
|
||||
} else if (pid == 0) {
|
||||
break;
|
||||
} else if (WIFEXITED(status)) {
|
||||
if (WEXITSTATUS(status) != 0)
|
||||
log_info("test: child %d exited with status "
|
||||
"%d\n", pid, WEXITSTATUS(status));
|
||||
else
|
||||
log_debug("test: child %d exited "
|
||||
"successfully\n", pid);
|
||||
} else if (WIFSIGNALED(status)) {
|
||||
log_debug("test: child %d exited by signal %d\n", pid,
|
||||
WTERMSIG(status));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void terminal_closed(struct kmscon_terminal *term, void *data)
|
||||
{
|
||||
#if 0
|
||||
/*
|
||||
* Alternativly, we could spwan a new login/shell here, like what
|
||||
* happens when the user exits the shell in a linux console:
|
||||
*/
|
||||
|
||||
int ret;
|
||||
struct app *app = data;
|
||||
|
||||
if (!app)
|
||||
goto err_out;
|
||||
|
||||
ret = kmscon_terminal_open(app->term, app->eloop,
|
||||
terminal_closed, app);
|
||||
if (ret)
|
||||
goto err_out;
|
||||
|
||||
return;
|
||||
|
||||
err_out:
|
||||
#endif
|
||||
terminate = 1;
|
||||
}
|
||||
|
||||
static void read_input(struct kmscon_input *input,
|
||||
struct kmscon_input_event *ev, void *data)
|
||||
{
|
||||
@ -131,6 +190,7 @@ static void destroy_app(struct app *app)
|
||||
kmscon_compositor_unref(app->comp);
|
||||
kmscon_font_factory_unref(app->ff);
|
||||
kmscon_symbol_table_unref(app->st);
|
||||
kmscon_eloop_rm_signal(app->sig_chld);
|
||||
kmscon_eloop_rm_signal(app->sig_int);
|
||||
kmscon_eloop_rm_signal(app->sig_term);
|
||||
kmscon_eloop_unref(app->eloop);
|
||||
@ -154,6 +214,11 @@ static int setup_app(struct app *app)
|
||||
if (ret)
|
||||
goto err_loop;
|
||||
|
||||
ret = kmscon_eloop_new_signal(app->eloop, &app->sig_chld, SIGCHLD,
|
||||
sig_chld, NULL);
|
||||
if (ret)
|
||||
goto err_loop;
|
||||
|
||||
ret = kmscon_symbol_table_new(&app->st);
|
||||
if (ret)
|
||||
goto err_loop;
|
||||
@ -186,7 +251,8 @@ static int setup_app(struct app *app)
|
||||
if (ret)
|
||||
goto err_loop;
|
||||
|
||||
ret = kmscon_terminal_connect_eloop(app->term, app->eloop);
|
||||
ret = kmscon_terminal_open(app->term, app->eloop,
|
||||
terminal_closed, app);
|
||||
if (ret)
|
||||
goto err_loop;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user