/*********************************************************/ /* ptycon: a pty-console bridge */ /* Copyright (C) 2016--2017 SysDeer Technologies, LLC */ /* Released under GPLv2 and GPLv3; see COPYING.PTYCON. */ /*********************************************************/ #include #include #include #include #include #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; }