Browse Source

pty: SIGWINCH

Serj Kalichev 1 year ago
parent
commit
d33c8c981a

+ 57 - 0
bin/klish/interactive.c

@@ -33,13 +33,18 @@ bool_t cmd_ack_cb(ktp_session_t *ktp, const faux_msg_t *msg, void *udata);
 bool_t cmd_incompleted_ack_cb(ktp_session_t *ktp, const faux_msg_t *msg, void *udata);
 bool_t completion_ack_cb(ktp_session_t *ktp, const faux_msg_t *msg, void *udata);
 bool_t help_ack_cb(ktp_session_t *ktp, const faux_msg_t *msg, void *udata);
+
 static bool_t stdin_cb(faux_eloop_t *eloop, faux_eloop_type_e type,
 	void *associated_data, void *user_data);
+static bool_t sigwinch_cb(faux_eloop_t *eloop, faux_eloop_type_e type,
+	void *associated_data, void *user_data);
+
 static bool_t ktp_sync_auth(ktp_session_t *ktp, int *retcode,
 	faux_error_t *error);
 static void reset_hotkey_table(ctx_t *ctx);
 static bool_t interactive_stdout_cb(ktp_session_t *ktp, const char *line, size_t len,
 	void *user_data);
+static bool_t send_winch_notification(ctx_t *ctx);
 
 // Keys
 static bool_t tinyrl_key_enter(tinyrl_t *tinyrl, unsigned char key);
@@ -109,7 +114,14 @@ int klish_interactive_shell(ktp_session_t *ktp, struct options *opts)
 	tinyrl_redisplay(tinyrl);
 
 	eloop = ktp_session_eloop(ktp);
+
+	// Notify server about terminal window size change
+	faux_eloop_add_signal(eloop, SIGWINCH, sigwinch_cb, &ctx);
+
 	faux_eloop_add_fd(eloop, STDIN_FILENO, POLLIN, stdin_cb, &ctx);
+
+	send_winch_notification(&ctx);
+
 	faux_eloop_loop(eloop);
 
 cleanup:
@@ -341,6 +353,51 @@ static bool_t stdin_cb(faux_eloop_t *eloop, faux_eloop_type_e type,
 }
 
 
+static bool_t send_winch_notification(ctx_t *ctx)
+{
+	size_t width = 0;
+	size_t height = 0;
+	char *winsize = NULL;
+	faux_msg_t *req = NULL;
+	ktp_status_e status = KTP_STATUS_NONE;
+
+	if (!ctx->tinyrl)
+		return BOOL_FALSE;
+	if (!ctx->ktp)
+		return BOOL_FALSE;
+
+	tinyrl_winsize(ctx->tinyrl, &width, &height);
+	winsize = faux_str_sprintf("%lu %lu", width, height);
+
+	req = ktp_msg_preform(KTP_NOTIFICATION, status);
+	faux_msg_add_param(req, KTP_PARAM_WINCH, winsize, strlen(winsize));
+	faux_str_free(winsize);
+	faux_msg_send_async(req, ktp_session_async(ctx->ktp));
+	faux_msg_free(req);
+
+	return BOOL_TRUE;
+}
+
+
+static bool_t sigwinch_cb(faux_eloop_t *eloop, faux_eloop_type_e type,
+	void *associated_data, void *udata)
+{
+	ctx_t *ctx = (ctx_t *)udata;
+
+	if (!ctx)
+		return BOOL_FALSE;
+
+	send_winch_notification(ctx);
+
+	// Happy compiler
+	eloop = eloop;
+	type = type;
+	associated_data = associated_data;
+
+	return BOOL_TRUE;
+}
+
+
 static bool_t tinyrl_key_enter(tinyrl_t *tinyrl, unsigned char key)
 {
 	const char *line = NULL;

+ 17 - 0
klish/kcontext.h

@@ -20,47 +20,64 @@ C_DECL_BEGIN
 // Type
 kcontext_type_e kcontext_type(const kcontext_t *context);
 FAUX_HIDDEN bool_t kcontext_set_type(kcontext_t *context, kcontext_type_e type);
+
 // Scheme
 kscheme_t *kcontext_scheme(const kcontext_t *context);
 bool_t kcontext_set_scheme(kcontext_t *context, kscheme_t *scheme);
+
 // RetCode
 int kcontext_retcode(const kcontext_t *context);
 FAUX_HIDDEN bool_t kcontext_set_retcode(kcontext_t *context, int retcode);
+
 // Plugin
 kplugin_t *kcontext_plugin(const kcontext_t *context);
 FAUX_HIDDEN bool_t kcontext_set_plugin(kcontext_t *context, kplugin_t *plugin);
+
 // Sym
 ksym_t *kcontext_sym(const kcontext_t *context);
 FAUX_HIDDEN bool_t kcontext_set_sym(kcontext_t *context, ksym_t *sym);
+
 // Pargv object
 kpargv_t *kcontext_pargv(const kcontext_t *context);
 FAUX_HIDDEN bool_t kcontext_set_pargv(kcontext_t *context, kpargv_t *pargv);
+
 // Parent pargv object
 const kpargv_t *kcontext_parent_pargv(const kcontext_t *context);
 FAUX_HIDDEN bool_t kcontext_set_parent_pargv(kcontext_t *context,
 	const kpargv_t *parent_pargv);
+
 // Action iterator
 faux_list_node_t *kcontext_action_iter(const kcontext_t *context);
 FAUX_HIDDEN bool_t kcontext_set_action_iter(kcontext_t *context, faux_list_node_t *action_iter);
+
 // STDIN
 int kcontext_stdin(const kcontext_t *context);
 FAUX_HIDDEN bool_t kcontext_set_stdin(kcontext_t *context, int stdin);
+
 // STDOUT
 int kcontext_stdout(const kcontext_t *context);
 FAUX_HIDDEN bool_t kcontext_set_stdout(kcontext_t *context, int stdout);
+
 // STDERR
 int kcontext_stderr(const kcontext_t *context);
 FAUX_HIDDEN bool_t kcontext_set_stderr(kcontext_t *context, int stderr);
+
 // PID
 pid_t kcontext_pid(const kcontext_t *context);
 FAUX_HIDDEN bool_t kcontext_set_pid(kcontext_t *context, pid_t pid);
+
 // Session
 ksession_t *kcontext_session(const kcontext_t *context);
 FAUX_HIDDEN bool_t kcontext_set_session(kcontext_t *context, ksession_t *session);
+
 // Done
 bool_t kcontext_done(const kcontext_t *context);
 FAUX_HIDDEN bool_t kcontext_set_done(kcontext_t *context, bool_t done);
 
+// Pseudo Terminal Slave filename
+const char *kcontext_pts_fn(const kcontext_t *context);
+bool_t kcontext_set_pts_fn(kcontext_t *context, const char *pts_fn);
+
 // Wrappers
 kparg_t *kcontext_candidate_parg(const kcontext_t *context);
 const kentry_t *kcontext_candidate_entry(const kcontext_t *context);

+ 2 - 0
klish/kexec.h

@@ -46,6 +46,7 @@ bool_t kexec_done(const kexec_t *exec);
 bool_t kexec_retcode(const kexec_t *exec, int *status);
 // Saved path
 kpath_t *kexec_saved_path(const kexec_t *exec);
+
 // CONTEXTs
 bool_t kexec_add_contexts(kexec_t *exec, kcontext_t *context);
 ssize_t kexec_contexts_len(const kexec_t *exec);
@@ -56,6 +57,7 @@ kcontext_t *kexec_contexts_each(kexec_contexts_node_t **iter);
 bool_t kexec_continue_command_execution(kexec_t *exec, pid_t pid, int wstatus);
 bool_t kexec_exec(kexec_t *exec);
 bool_t kexec_interactive(const kexec_t *exec);
+bool_t kexec_set_winsize(kexec_t *exec);
 
 
 C_DECL_END

+ 8 - 0
klish/ksession.h

@@ -26,6 +26,14 @@ kpath_t *ksession_path(const ksession_t *session);
 bool_t ksession_done(const ksession_t *session);
 bool_t ksession_set_done(ksession_t *session, bool_t done);
 
+// Width of pseudo terminal
+size_t ksession_term_width(const ksession_t *session);
+bool_t ksession_set_term_width(ksession_t *session, size_t term_width);
+
+// Height of pseudo terminal
+size_t ksession_term_height(const ksession_t *session);
+bool_t ksession_set_term_height(ksession_t *session, size_t term_height);
+
 C_DECL_END
 
 #endif // _klish_ksession_h

+ 7 - 0
klish/ksession/kcontext.c

@@ -32,6 +32,7 @@ struct kcontext_s {
 	int stderr;
 	pid_t pid;
 	bool_t done; // If all actions are done
+	char *pts_fn; // Pseudo Terminal Slave file name
 };
 
 
@@ -92,6 +93,10 @@ FAUX_HIDDEN KSET(context, ksession_t *, session);
 KGET_BOOL(context, done);
 FAUX_HIDDEN KSET_BOOL(context, done);
 
+// PTS file name (Pseudo Terminal Slave)
+KSET_STR(context, pts_fn);
+KGET_STR(context, pts_fn);
+
 
 kcontext_t *kcontext_new(kcontext_type_e type)
 {
@@ -117,6 +122,7 @@ kcontext_t *kcontext_new(kcontext_type_e type)
 	context->pid = -1; // PID of currently executed ACTION
 	context->session = NULL; // Don't free
 	context->done = BOOL_FALSE;
+	context->pts_fn = NULL;
 
 	return context;
 }
@@ -135,6 +141,7 @@ void kcontext_free(kcontext_t *context)
 		close(context->stdout);
 	if (context->stderr != -1)
 		close(context->stderr);
+	faux_str_free(context->pts_fn);
 
 	faux_free(context);
 }

+ 62 - 18
klish/ksession/kexec.c

@@ -10,6 +10,8 @@
 #include <unistd.h>
 #include <fcntl.h>
 #include <syslog.h>
+#include <sys/ioctl.h>
+#include <termios.h>
 
 #include <faux/list.h>
 #include <faux/buf.h>
@@ -228,6 +230,48 @@ bool_t kexec_add(kexec_t *exec, kcontext_t *context)
 }
 
 
+bool_t kexec_set_winsize(kexec_t *exec)
+{
+	int fd = -1;
+	size_t width = 0;
+	size_t height = 0;
+	struct winsize ws = {};
+	int res = -1;
+	kcontext_t *context = NULL;
+	ksession_t *session = NULL;
+
+	if (!exec)
+		return BOOL_FALSE;
+	if (!kexec_interactive(exec))
+		return BOOL_FALSE;
+	fd = kexec_stdin(exec);
+	if (fd < 0)
+		return BOOL_FALSE;
+	if (!isatty(fd))
+		return BOOL_FALSE;
+	context = (kcontext_t *)faux_list_data(faux_list_head(exec->contexts));
+	if (!context)
+		return BOOL_FALSE;
+	session = kcontext_session(context);
+	if (!session)
+		return BOOL_FALSE;
+
+	// Set pseudo terminal window size
+	width = ksession_term_width(session);
+	height = ksession_term_height(session);
+	if ((width == 0) || (height == 0))
+		return BOOL_FALSE;
+
+	ws.ws_col = (unsigned short)width;
+	ws.ws_row = (unsigned short)height;
+	res = ioctl(fd, TIOCSWINSZ, &ws);
+	if (res < 0)
+		return BOOL_FALSE;
+
+	return BOOL_TRUE;
+}
+
+
 static bool_t kexec_prepare(kexec_t *exec)
 {
 	int pipefd[2] = {};
@@ -247,12 +291,10 @@ static bool_t kexec_prepare(kexec_t *exec)
 	if (kexec_interactive(exec)) {
 		int ptm = -1;
 		char *pts_name = NULL;
-		int pts = -1;
 		kcontext_t *context = (kcontext_t *)faux_list_data(
 			faux_list_head(exec->contexts));
 
 		ptm = open(PTMX_PATH, O_RDWR, O_NOCTTY);
-//		ptm = open(PTMX_PATH, O_RDWR, 0);
 		if (ptm < 0)
 			return BOOL_FALSE;
 		// Set O_NONBLOCK flag here. Because this flag is ignored while
@@ -262,19 +304,16 @@ static bool_t kexec_prepare(kexec_t *exec)
 		grantpt(ptm);
 		unlockpt(ptm);
 		pts_name = ptsname(ptm);
-		pts = open(pts_name, O_RDWR, O_NOCTTY);
-//		pts = open(pts_name, O_RDWR, 0);
-		if (pts < 0) {
-			close(ptm);
-			return BOOL_FALSE;
-		}
 
 		kexec_set_stdin(exec, ptm);
 		kexec_set_stdout(exec, ptm);
 		kexec_set_stderr(exec, ptm);
-		kcontext_set_stdin(context, pts);
-		kcontext_set_stdout(context, pts);
-		kcontext_set_stderr(context, pts);
+		// Don't set pts fd here. In a case of pseudo-terminal the pts
+		// must be opened later in the child after setsid(). So just
+		// save filename of pts.
+		kcontext_set_pts_fn(context, pts_name);
+		// Set pseudo terminal window size
+		kexec_set_winsize(exec);
 
 		return BOOL_TRUE;
 	}
@@ -455,6 +494,9 @@ static bool_t exec_action_async(kcontext_t *context, const kaction_t *action,
 	ksym_fn fn = NULL;
 	int exitcode = 0;
 	pid_t child_pid = -1;
+	int i = 0;
+	int fdmax = 0;
+	const char *pts_fn = NULL;
 
 	fn = ksym_function(kaction_sym(action));
 
@@ -480,13 +522,12 @@ static bool_t exec_action_async(kcontext_t *context, const kaction_t *action,
 	}
 
 	// Child
-	if (kaction_interactive(action)) {
+	if ((pts_fn = kcontext_pts_fn(context)) != NULL) {
 		int fd = -1;
-		char *tmp = NULL;
 		setsid();
-		tmp = faux_str_sprintf("/proc/self/fd/%i", kcontext_stdin(context));
-		fd = open(tmp, O_RDWR, 0);
-		faux_str_free(tmp);
+		fd = open(pts_fn, O_RDWR, 0);
+		if (fd < 0)
+			_exit(-1);
 		dup2(fd, STDIN_FILENO);
 		dup2(fd, STDOUT_FILENO);
 		dup2(fd, STDERR_FILENO);
@@ -495,9 +536,12 @@ static bool_t exec_action_async(kcontext_t *context, const kaction_t *action,
 		dup2(kcontext_stdout(context), STDOUT_FILENO);
 		dup2(kcontext_stderr(context), STDERR_FILENO);
 	}
-	int i = 0;
-	for (i = 3; i < 256; i++)
+
+	// Close all inherited fds except stdin, stdout, stderr
+	fdmax = (int)sysconf(_SC_OPEN_MAX);
+	for (i = (STDERR_FILENO + 1); i < fdmax; i++)
 		close(i);
+
 	exitcode = fn(context);
 	// We will use _exit() later so stdio streams will remain unflushed.
 	// Some output data can be lost. Flush necessary streams here.

+ 12 - 0
klish/ksession/ksession.c

@@ -15,6 +15,8 @@ struct ksession_s {
 	kscheme_t *scheme;
 	kpath_t *path;
 	bool_t done; // Indicates that session is over and must be closed
+	size_t term_width;
+	size_t term_height;
 };
 
 
@@ -28,6 +30,14 @@ KGET(session, kpath_t *, path);
 KGET_BOOL(session, done);
 KSET_BOOL(session, done);
 
+// Width of pseudo terminal
+KGET(session, size_t, term_width);
+KSET(session, size_t, term_width);
+
+// Height of pseudo terminal
+KGET(session, size_t, term_height);
+KSET(session, size_t, term_height);
+
 
 ksession_t *ksession_new(kscheme_t *scheme, const char *start_entry)
 {
@@ -68,6 +78,8 @@ ksession_t *ksession_new(kscheme_t *scheme, const char *start_entry)
 	assert(level);
 	kpath_push(session->path, level);
 	session->done = BOOL_FALSE;
+	session->term_width = 0;
+	session->term_height = 0;
 
 	return session;
 }

+ 2 - 1
klish/ktp.h

@@ -35,7 +35,8 @@ typedef enum {
 	KTP_PARAM_LINE = 'L',
 	KTP_PARAM_PREFIX = 'P', // Same as line but differ by meaning
 	KTP_PARAM_PROMPT = '$', // Same as line but differ by meaning
-	KTP_PARAM_HOTKEY = 'H', // key '\0' cmd
+	KTP_PARAM_HOTKEY = 'H', // <key>'\0'<cmd>
+	KTP_PARAM_WINCH = 'W', // <width><space><height>
 	KTP_PARAM_ERROR = 'E',
 	KTP_PARAM_RETCODE = 'R',
 } ktp_param_e;

+ 10 - 0
klish/ktp/ktp_session.c

@@ -228,6 +228,16 @@ int ktp_session_fd(const ktp_session_t *ktp)
 }
 
 
+faux_async_t *ktp_session_async(const ktp_session_t *ktp)
+{
+	assert(ktp);
+	if (!ktp)
+		return NULL;
+
+	return ktp->async;
+}
+
+
 static bool_t server_ev(faux_eloop_t *eloop, faux_eloop_type_e type,
 	void *associated_data, void *user_data)
 {

+ 55 - 0
klish/ktp/ktpd_session.c

@@ -14,6 +14,7 @@
 #include <sys/wait.h>
 
 #include <faux/str.h>
+#include <faux/conv.h>
 #include <faux/async.h>
 #include <faux/msg.h>
 #include <faux/eloop.h>
@@ -905,6 +906,57 @@ static bool_t ktpd_session_process_stdin(ktpd_session_t *ktpd, faux_msg_t *msg)
 }
 
 
+static bool_t ktpd_session_process_winch(ktpd_session_t *ktpd, faux_msg_t *msg)
+{
+	char *line = NULL;
+	char *p = NULL;
+	unsigned short width = 0;
+	unsigned short height = 0;
+
+	assert(ktpd);
+	assert(msg);
+
+	if (!(line = faux_msg_get_str_param_by_type(msg, KTP_PARAM_WINCH)))
+		return BOOL_TRUE;
+
+	p = strchr(line, ' ');
+	if (!p || (p == line)) {
+		faux_str_free(line);
+		return BOOL_FALSE;
+	}
+	if (!faux_conv_atous(line, &width, 0)) {
+		faux_str_free(line);
+		return BOOL_FALSE;
+	}
+	if (!faux_conv_atous(p + 1, &height, 0)) {
+		faux_str_free(line);
+		return BOOL_FALSE;
+	}
+
+	ksession_set_term_width(ktpd->session, width);
+	ksession_set_term_height(ktpd->session, height);
+	faux_str_free(line);
+
+	if (!ktpd->exec)
+		return BOOL_TRUE;
+	// Set pseudo terminal window size
+	kexec_set_winsize(ktpd->exec);
+
+	return BOOL_TRUE;
+}
+
+
+static bool_t ktpd_session_process_notification(ktpd_session_t *ktpd, faux_msg_t *msg)
+{
+	assert(ktpd);
+	assert(msg);
+
+	ktpd_session_process_winch(ktpd, msg);
+
+	return BOOL_TRUE;
+}
+
+
 static bool_t ktpd_session_dispatch(ktpd_session_t *ktpd, faux_msg_t *msg)
 {
 	uint16_t cmd = 0;
@@ -944,6 +996,9 @@ static bool_t ktpd_session_dispatch(ktpd_session_t *ktpd, faux_msg_t *msg)
 			break;
 		ktpd_session_process_stdin(ktpd, msg);
 		break;
+	case KTP_NOTIFICATION:
+		ktpd_session_process_notification(ktpd, msg);
+		break;
 	default:
 		syslog(LOG_WARNING, "Unsupported command: 0x%04u\n", cmd);
 		break;

+ 1 - 0
klish/ktp_session.h

@@ -83,6 +83,7 @@ bool_t ktp_session_set_cb(ktp_session_t *ktp, ktp_session_cb_e cb_id,
 	void *fn, void *udata);
 bool_t ktp_session_connected(ktp_session_t *session);
 int ktp_session_fd(const ktp_session_t *session);
+faux_async_t *ktp_session_async(const ktp_session_t *ktp);
 bool_t ktp_session_stop_on_answer(const ktp_session_t *ktp);
 bool_t ktp_session_set_stop_on_answer(ktp_session_t *ktp, bool_t stop_on_answer);
 ktp_session_state_e ktp_session_state(const ktp_session_t *ktp);

+ 4 - 0
tinyrl/tinyrl.h

@@ -97,7 +97,11 @@ void tinyrl_reset_line_state(tinyrl_t *tinyrl);
 void tinyrl_reset_line(tinyrl_t *tinyrl);
 void tinyrl_crlf(const tinyrl_t *tinyrl);
 void tinyrl_multi_crlf(const tinyrl_t *tinyrl);
+
+void tinyrl_winsize(const tinyrl_t *tinyrl, size_t *width, size_t *height);
 size_t tinyrl_width(const tinyrl_t *tinyrl);
+size_t tinyrl_height(const tinyrl_t *tinyrl);
+
 int tinyrl_printf(const tinyrl_t *tinyrl, const char *fmt, ...);
 size_t tinyrl_equal_part(const tinyrl_t *tinyrl,
 	const char *s1, const char *s2);

+ 27 - 4
tinyrl/tinyrl/tinyrl.c

@@ -729,13 +729,36 @@ void tinyrl_reset_hist_pos(tinyrl_t *tinyrl)
 }
 
 
+void tinyrl_winsize(const tinyrl_t *tinyrl, size_t *width, size_t *height)
+{
+	vt100_t *term = NULL;
+
+	if (tinyrl)
+		term = tinyrl->term;
+
+	vt100_winsize(term, width, height);
+}
+
+
 size_t tinyrl_width(const tinyrl_t *tinyrl)
 {
-	assert(tinyrl);
-	if (!tinyrl)
-		return 80;
+	vt100_t *term = NULL;
+
+	if (tinyrl)
+		term = tinyrl->term;
+
+	return vt100_width(term);
+}
+
+
+size_t tinyrl_height(const tinyrl_t *tinyrl)
+{
+	vt100_t *term = NULL;
+
+	if (tinyrl)
+		term = tinyrl->term;
 
-	return tinyrl->width;
+	return vt100_height(term);
 }
 
 

+ 1 - 0
tinyrl/vt100.h

@@ -79,6 +79,7 @@ void vt100_set_istream(vt100_t *vt100, FILE *istream);
 FILE *vt100_ostream(const vt100_t *vt100);
 void vt100_set_ostream(vt100_t *vt100, FILE *ostream);
 
+void vt100_winsize(const vt100_t *vt100, size_t *width, size_t *height);
 size_t vt100_width(const vt100_t *vt100);
 size_t vt100_height(const vt100_t *vt100);
 

+ 29 - 28
tinyrl/vt100/vt100.c

@@ -253,49 +253,50 @@ int vt100_eof(const vt100_t *vt100)
 }
 
 
-size_t vt100_width(const vt100_t *vt100)
+void vt100_winsize(const vt100_t *vt100, size_t *width, size_t *height)
 {
 #ifdef TIOCGWINSZ
 	struct winsize ws = {};
 	int res = 0;
 #endif
-	const size_t default_width = 80;
-
-	if(!vt100 || !vt100->ostream)
-		return default_width;
+	size_t w = 80;
+	size_t h = 25;
 
 #ifdef TIOCGWINSZ
-	ws.ws_col = 0;
-	res = ioctl(fileno(vt100->ostream), TIOCGWINSZ, &ws);
-	if (res || (0 == ws.ws_col))
-		return default_width;
-	return (size_t)ws.ws_col;
-#else
-	return default_width;
+	if(vt100 && vt100->ostream && isatty(fileno(vt100->ostream))) {
+		res = ioctl(fileno(vt100->ostream), TIOCGWINSZ, &ws);
+		if (!res) {
+			if (0 != ws.ws_col)
+				w = (size_t)ws.ws_col;
+			if (0 != ws.ws_row)
+				h = (size_t)ws.ws_row;
+		}
+	}
 #endif
+
+	if (width)
+		*width = w;
+	if (height)
+		*height = h;
+}
+
+size_t vt100_width(const vt100_t *vt100)
+{
+	size_t w = 0;
+
+	vt100_winsize(vt100, &w, NULL);
+
+	return w;
 }
 
 
 size_t vt100_height(const vt100_t *vt100)
 {
-#ifdef TIOCGWINSZ
-	struct winsize ws = {};
-	int res = 0;
-#endif
-	const size_t default_height = 25;
+	size_t h = 0;
 
-	if(!vt100 || !vt100->ostream)
-		return default_height;
+	vt100_winsize(vt100, NULL, &h);
 
-#ifdef TIOCGWINSZ
-	ws.ws_row = 0;
-	res = ioctl(fileno(vt100->ostream), TIOCGWINSZ, &ws);
-	if (res || (0 == ws.ws_row))
-		return default_height;
-	return (size_t)ws.ws_row;
-#else
-	return default_height;
-#endif
+	return h;
 }