Blob Blame History Raw
/*********************************************************/
/*  ptycon: a pty-console bridge                         */
/*  Copyright (C) 2016  Z. Gilboa                        */
/*  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"

typedef nt_unicode_conversion_params_utf16_to_utf8 uc_conv_params;

extern const struct ptyc_vkcode ptyc_vkcode[0x100];

static size_t ptyc_repeat(unsigned char * ch, size_t n, uint16_t count)
{
	unsigned char * dst;
	int		i;

	if (!--count)
		return n;

	if (n == 1) {
		for (i=count, dst=&ch[n]; i; i--) {
			dst[0] = ch[0];
			dst = &dst[n];
		}

	} else if (n == 2) {
		for (i=count, dst=&ch[n]; i; i--) {
			dst[0] = ch[0];
			dst[1] = ch[1];
			dst = &dst[n];
		}

	} else if (n == 3) {
		for (i=count, dst=&ch[n]; i; i--) {
			dst[0] = ch[0];
			dst[1] = ch[1];
			dst[2] = ch[2];
			dst = &dst[n];
		}

	} else if (n == 4) {
		for (i=count, dst=&ch[n]; i; i--) {
			dst[0] = ch[0];
			dst[1] = ch[1];
			dst[2] = ch[2];
			dst[3] = ch[3];
			dst = &dst[n];
		}
	}

	return n*count;
}

static size_t ptyc_translate_virtual_keycode(
	nt_input_record *	rec,
	unsigned char *		ch)
{
	ntapi->tt_generic_memcpy(
		ch,
		ptyc_vkcode[rec->key_event.virtual_key_code].mbstr,
		ptyc_vkcode[rec->key_event.virtual_key_code].mblen);

	return ptyc_repeat(
		ch,
		ptyc_vkcode[rec->key_event.virtual_key_code].mblen,
		rec->key_event.repeat_count);
}

static size_t ptyc_translate_keyboard_event(
	nt_input_record *	rec,
	unsigned char *		ch,
	wchar16_t *		wch)
{
	wchar16_t	src[2];
	wchar16_t	recwch;
	uint32_t	nbytes;
	uc_conv_params	params = {0,0,0,0,0,0,0,0,0};

	if (!rec->key_event.key_down)
		return 0;

	if (!(recwch = rec->key_event.unicode_char))
		return ptyc_translate_virtual_keycode(
			rec,ch);

	if (*wch) {
		if ((recwch < 0xDC00) || (recwch >= 0xE000)) {
			*wch = 0;
			return 0;
		} else {
			src[0] = *wch;
			src[1] = recwch;
			*wch   = 0;
			nbytes = 2*sizeof(wchar16_t);
		}
	} else if ((recwch >= 0xD800) && (recwch < 0xDC00)) {
		*wch = recwch;
		return 0;

	} else {
		src[0] = recwch;
		nbytes = sizeof(wchar16_t);
	}

	params.src		 = src;
	params.src_size_in_bytes = nbytes;
	params.dst		 = ch;
	params.dst_size_in_bytes = 4;

	ntapi->uc_convert_unicode_stream_utf16_to_utf8(
		&params);

	return ptyc_repeat(
		ch,
		params.bytes_written,
		rec->key_event.repeat_count);
}

static size_t ptyc_translate_mouse_event(
	nt_input_record *	rec,
	unsigned char *		ch)
{
	(void)rec;
	(void)ch;
	return 0;
}

static size_t ptyc_translate_window_event(nt_input_record * rec)
{
	(void)rec;
	return 0;
}

static size_t ptyc_translate_menu_event(nt_input_record * rec)
{
	(void)rec;
	return 0;
}

static size_t ptyc_translate_focus_event(nt_input_record * rec)
{
	(void)rec;
	return 0;
}

static size_t ptyc_translate_event(
	nt_input_record *	rec,
	unsigned char *		ch,
	wchar16_t *		wch)
{
	switch (rec->event_type) {
		case NT_KEY_EVENT:
			return ptyc_translate_keyboard_event(
				rec,ch,wch);

		case NT_MOUSE_EVENT:
			return ptyc_translate_mouse_event(
				rec,ch);

		case NT_WINDOW_BUFFER_SIZE_EVENT:
			return ptyc_translate_window_event(rec);

		case NT_MENU_EVENT:
			return ptyc_translate_menu_event(rec);

		case NT_FOCUS_EVENT:
			return ptyc_translate_focus_event(rec);
	}

	return 0;
}

int __stdcall ptyc_console_reader(struct ptyc_driver_ctx_impl * ictx)
{
	int32_t		status;
	void *		hwait;
	nt_iosb		iosb;
	uint32_t	nevents;
	size_t		nbytes;
	wchar16_t	wch;
	unsigned char *	ch;
	unsigned	i;

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

	do {
		if (!(ntcon->read_console_input_utf16(
				ictx->tctx.hin,
				ictx->tctx.input.events,
				PTYC_RAW_EVENTS,
				&nevents)))
			return NT_STATUS_ALREADY_DISCONNECTED;

		for (ch=ictx->tctx.input.stream, i=0; i<nevents; i++)
			ch += ptyc_translate_event(
				&ictx->tctx.input.events[i],
				ch,&wch);

		nbytes = ch - ictx->tctx.input.stream;
		ch     = ictx->tctx.input.stream;

		for (; nbytes; ) {
			status = ntapi->pty_write(
				ictx->cctx.hptm,
				hwait,0,0,
				&iosb,
				ch,nbytes,
				0,0);

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

			if (status || iosb.status) {
				ntapi->zw_close(hwait);
				return status ? status : iosb.status;
			}

			ch     += iosb.info;
			nbytes -= iosb.info;
		}
	} while (1);

	return NT_STATUS_INTERNAL_ERROR;
}