Blob Blame History Raw
/*********************************************************/
/*  ptycon: a pty-console bridge                         */
/*  Copyright (C) 2016--2017  SysDeer Technologies, LLC  */
/*  Released under GPLv2 and GPLv3; see COPYING.PTYCON.  */
/*********************************************************/

#include <psxtypes/psxtypes.h>
#include <ntcon/ntcon.h>
#include <ntapi/ntapi.h>
#include <gdi/gdi.h>

#include <ptycon/ptycon.h>
#include "ptycon_driver_impl.h"
#include "ptycon_status_impl.h"

static ptyc_term_handler	ptyc_screen_handler;
static ptyc_term_handler	ptyc_ctrl_handler;
static ptyc_term_handler	ptyc_flush_screen_buffer;

int __stdcall ptyc_console_writer(struct ptyc_driver_ctx_impl * ictx)
{
	int32_t						status;
	nt_iosb						iosb;
	size_t						nsteps;
	void *						hwait;
	unsigned char *					inbuf;
	struct ptyc_term_ctx *				tctx;
	nt_unicode_conversion_params_utf8_to_utf16	uc_params;

	if ((status = ntapi->tt_create_private_event(
			&hwait,
			NT_NOTIFICATION_EVENT,
			NT_EVENT_NOT_SIGNALED)))
		return status;

	tctx				= &ictx->tctx;
	inbuf				= (unsigned char *)&tctx->data.in[1];

	uc_params.dst_size_in_bytes	= sizeof(tctx->data.out);
	uc_params.leftover_count	= 0;
	uc_params.leftover_bytes	= 0;

	tctx->handler			= ptyc_screen_handler;
	tctx->char_handler		= ptyc_screen_handler;
	tctx->ctrl_handler		= ptyc_ctrl_handler;
	tctx->ctrl_handlers[0]		= ptyc_esi_handlers;
	tctx->wch_con			= tctx->data.screen;

	do {
		uc_params.code_points	= 0;
		uc_params.addr_failed	= 0;
		uc_params.bytes_written	= 0;

		status = ntapi->pty_read(
			ictx->cctx.hptm,
			hwait,0,0,
			&iosb,
			inbuf,
			sizeof(tctx->data.in) - sizeof(uintptr_t),
			0,0);

		if (status == NT_STATUS_PENDING)
			if (!(status = ntapi->zw_wait_for_single_object(
					hwait,NT_SYNC_ALERTABLE,0)))
				status = iosb.status;

		if (status)
			return ptyc_set_status(&ictx->ctx,status);

		/* account for leftover bytes from the previous conversion */
		tctx->data.in[0]		= uc_params.leftover_bytes;
		uc_params.src			= inbuf - uc_params.leftover_count;
		uc_params.src_size_in_bytes	= iosb.info + uc_params.leftover_count;
		uc_params.dst			= tctx->data.out;


		/* utf-8 --> utf-16 */
		status = ntapi->uc_convert_unicode_stream_utf8_to_utf16(
			&uc_params);

		/* process the converted (utf-16) stream */
		tctx->wch_pty = tctx->data.out;
		nsteps        = uc_params.bytes_written / sizeof(wchar16_t);

		for (; nsteps; nsteps--)
			tctx->handler = (ptyc_term_handler *)tctx->handler(tctx);

		/* output to console */
		ptyc_flush_screen_buffer(tctx);
	} while (1);

	return NT_STATUS_INTERNAL_ERROR;
}


static void * __fastcall ptyc_screen_handler(struct ptyc_term_ctx * tctx)
{
	if (*tctx->wch_pty == '\x1b') {
		/* output all pending characters */
		ptyc_flush_screen_buffer(tctx);

		/* advance stream pointer */
		tctx->wch_pty++;

		/* set initial control state */
		tctx->ctrl_state = PTYC_CTRL_STATE_ESI;
		tctx->ctrl_cap   = PTYC_ESI_ARRAY_SIZE;

		/* switch mode */
		return tctx->ctrl_handler;
	} else {
		/* filter out bell key? */
		if (*tctx->wch_pty == 0x7)
			if (!(tctx->drvflags & PTYC_DRIVER_BELL))
				return ptyc_screen_handler;

		/* copy character to the screen buffer */
		*tctx->wch_con = *tctx->wch_pty;

		/* advance the screen and stream pointers */
		tctx->wch_con++;
		tctx->wch_pty++;

		/* retain mode */
		return ptyc_screen_handler;
	}
}


static void * __fastcall ptyc_ctrl_handler(struct ptyc_term_ctx * tctx)
{
	ptyc_term_handler * const *	handlers;
	ptyc_term_handler *		handler;
	unsigned			idx;

	idx = (*tctx->wch_pty < tctx->ctrl_cap)
			? *tctx->wch_pty
			: tctx->ctrl_cap - 1;

	handlers = tctx->ctrl_handlers[tctx->ctrl_state];
	handler  = handlers[idx];

	return handler(tctx);
}


static void * __fastcall ptyc_flush_screen_buffer(struct ptyc_term_ctx * tctx)
{
	uint32_t ncunits;
	uint32_t nwritten;

	if (!(ncunits = (uint32_t)(tctx->wch_con - tctx->data.screen)))
		return tctx->wch_con;

	ntcon->write_console_utf16(
		tctx->hout,
		tctx->data.screen,
		ncunits,
		&nwritten,
		0);

	/* heuristics: interactive input? */
	if (ncunits <= 4) {
		ntcon->get_console_screen_buffer_info(
			tctx->hout,
			&tctx->screen_info);

		ntcon->set_console_cursor_position(
			tctx->hout,
			tctx->screen_info.cursor_position);
	}

	tctx->wch_con = tctx->data.screen;

	return tctx->wch_con;
}