Skip to content

Commit

Permalink
lib: add terminal resize capabilities
Browse files Browse the repository at this point in the history
This adds a new size() global, which actually lives on the process.term for
each process.  One or both dimensions can be changed at a time, or both
arguments can be omitted to return the current size.
  • Loading branch information
kevans91 committed Sep 20, 2024
1 parent 3794bd3 commit 0978755
Show file tree
Hide file tree
Showing 8 changed files with 173 additions and 1 deletion.
1 change: 1 addition & 0 deletions lib/core/porch_lua.c
Original file line number Diff line number Diff line change
Expand Up @@ -663,6 +663,7 @@ porchlua_process_term_set(porch_ipc_t ipc __unused, struct porch_ipc_msg *msg,

memcpy(parent_termios, child_termios, sizeof(*child_termios));
term->initialized = true;
term->winsz_valid = false;

return (0);
}
Expand Down
66 changes: 66 additions & 0 deletions lib/core/porch_tty.c
Original file line number Diff line number Diff line change
Expand Up @@ -319,10 +319,76 @@ porchlua_term_update(lua_State *L)
return (2);
}

static int
porchlua_term_size(lua_State *L)
{
struct porch_term *self;
lua_Number val;
bool fetching;

self = luaL_checkudata(L, 1, ORCHLUA_TERMHANDLE);
if (!self->winsz_valid) {
if (ioctl(self->proc->termctl, TIOCGWINSZ, &self->winsz) != 0) {
int error = errno;

luaL_pushfail(L);
lua_pushstring(L, strerror(error));
return (2);
}

self->winsz_valid = true;
}

/*
* If size doesn't have both width and height arguments, it simply
* return the current size.
*/
fetching = lua_isnoneornil(L, 2) && lua_isnoneornil(L, 3);
if (!fetching) {
if (!lua_isnoneornil(L, 2)) {
val = luaL_checknumber(L, 2);
if (val < 0 || val > USHRT_MAX) {
luaL_pushfail(L);
lua_pushfstring(L, "width out of bounds: %llu\n",
(uint64_t)val);
return (2);
}

self->winsz.ws_col = val;
}

if (!lua_isnoneornil(L, 3)) {
val = luaL_checknumber(L, 3);
if (val < 0 || val > USHRT_MAX) {
luaL_pushfail(L);
lua_pushfstring(L, "height out of bounds: %llu\n",
(uint64_t)val);
return (2);
}

self->winsz.ws_row = val;
}

if (ioctl(self->proc->termctl, TIOCSWINSZ, &self->winsz) != 0) {
int error = errno;

luaL_pushfail(L);
lua_pushstring(L, strerror(error));
return (2);
}
}

lua_pushnumber(L, self->winsz.ws_col);
lua_pushnumber(L, self->winsz.ws_row);

return (2);
}

#define ORCHTERM_SIMPLE(n) { #n, porchlua_term_ ## n }
static const luaL_Reg porchlua_term[] = {
ORCHTERM_SIMPLE(fetch),
ORCHTERM_SIMPLE(update),
ORCHTERM_SIMPLE(size),
{ NULL, NULL },
};

Expand Down
6 changes: 6 additions & 0 deletions lib/porch/scripter.lua
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,12 @@ function scripter.env.matcher(val)
return true
end

function scripter.env.size(w, h)
local current_process = current_ctx.process

return current_process.term:size(w, h)
end

function scripter.env.timeout(val)
if val == nil or val < 0 then
error("Timeout must be >= 0")
Expand Down
3 changes: 3 additions & 0 deletions lib/porch_lib.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#pragma once

#include <sys/types.h>
#include <sys/ioctl.h>

#include <stdbool.h>
#include <termios.h>
Expand Down Expand Up @@ -53,8 +54,10 @@ struct porch_process {

struct porch_term {
struct termios term;
struct winsize winsz;
struct porch_process *proc;
bool initialized;
bool winsz_valid;
};

struct porchlua_tty_cntrl {
Expand Down
21 changes: 20 additions & 1 deletion man/orch.5
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
.\"
.\" SPDX-License-Identifier: BSD-2-Clause
.\"
.Dd February 10, 2024
.Dd September 19, 2024
.Dt ORCH 5
.Os
.Sh NAME
Expand Down Expand Up @@ -236,6 +236,25 @@ table's keys correspond to supported characters, e.g.,
and the associated values are all truthy to indicate that they are supported.
.Pp
This directive is enqueued, not processed immediately.
.It Fn size "width" "height"
Set or get the size of the terminal associated with the process.
If at least one of
.Fa width
or
.Fa height
are not nil, then
.Fn size
will resize that dimension of the window.
The new current size of the window is always returned.
.Pp
The window will start off on a fresh spawn with a width and height of 0.
The size of the window is never persisted across processes.
.Pp
This directive is always processed immediately, and thus should always be used
in either an
.Fn enqueue
or
fail context.
.It Fn raw "boolean"
Changes the raw
.Fn write
Expand Down
30 changes: 30 additions & 0 deletions tests/resize_basic.orch
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
spawn("resized.sh")
match "ready"

enqueue(function()
local w, h = assert(size())

-- pty sizes start at 0 when we spawn off.
assert(w == 0, "width is wrong, expected 0 got " .. w)
assert(h == 0, "height is wrong, expected 0 got " .. h)

w, h = size(w + 25, h + 80)

-- Make sure that it's returning the *new* width and height when we set
-- them.
assert(w == 25, "width is wrong, expected 25 got " .. w)
assert(h == 80, "height is wrong, expected 80 got " .. h)

-- And not setting anything should still return the current dimensions.
w, h = size()
assert(w == 25, "width is wrong, expected 25 got " .. w)
assert(h == 80, "height is wrong, expected 80 got " .. h)
w, h = size(nil, nil)
assert(w == 25, "width is wrong, expected 25 got " .. w)
assert(h == 80, "height is wrong, expected 80 got " .. h)
end)

match "resized"
write "^C"

match "1"
25 changes: 25 additions & 0 deletions tests/resize_optional.orch
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
spawn("resized.sh")
match "ready"

enqueue(function()
local w, h = assert(size())

assert(w == 0, "width is wrong, expected 0 got " .. w)
assert(h == 0, "height is wrong, expected 0 got " .. h)

w, h = size(nil, h + 80)

assert(w == 0, "width is wrong, expected 0 got " .. w)
assert(h == 80, "height is wrong, expected 80 got " .. h)
end)

match "resized"

enqueue(function()
local w, h = size(25)
assert(w == 25, "width is wrong, expected 25 got " .. w)
assert(h == 80, "height is wrong, expected 80 got " .. h)
end)

write "^C"
match "2"
22 changes: 22 additions & 0 deletions tests/resized.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/bin/sh

loop=1

trap 'echo resized; caught=$((caught + 1))' WINCH
trap 'loop=0' INT

# Release the hounds now that the signal handler is setup.
echo "ready"

caught=0

while [ "$loop" -ne 0 ]; do
sleep 1
done

if [ "$caught" -eq 0 ]; then
1>&2 echo "Did not observe SIGWINCH"
exit 1
fi

echo "$caught"

0 comments on commit 0978755

Please sign in to comment.