/*
 * PROJECT:     ReactOS VT100 emulator
 * LICENSE:     GPL - See COPYING in the top level directory
 * FILE:        drivers/base/green/screen.c
 * PURPOSE:     IRP_MJ_PNP operations
 * PROGRAMMERS: Copyright 2005 Eric Kohl (ekohl@abo.rhein-zeitung.de)
 *              Copyright 2005 Art Yerkes
 *              Copyright 2005-2006 Hervé Poussineau (hpoussin@reactos.org)
 */

#include "green.h"

#define NDEBUG
#include <debug.h>

#define ESC       ((UCHAR)0x1b)

/* Force a move of the cursor on each printer char.
 * Very useful for debug, but it is very slow...
 */
//#define FORCE_POSITION

/* UCHAR is promoted to int when passed through '...',
 * so we get int with va_arg and cast them back to UCHAR.
 */
static VOID
AddToSendBuffer(
	IN PSCREEN_DEVICE_EXTENSION DeviceExtension,
	IN ULONG NumberOfChars,
	... /* IN int */)
{
	PIRP Irp;
	IO_STATUS_BLOCK ioStatus;
	va_list args;
	PDEVICE_OBJECT SerialDevice;
	ULONG SizeLeft;
	int CurrentInt;
	UCHAR CurrentChar;
	NTSTATUS Status;
	LARGE_INTEGER ZeroOffset;

	ZeroOffset.QuadPart = 0;

	SizeLeft = sizeof(DeviceExtension->SendBuffer) - DeviceExtension->SendBufferPosition;
	if (SizeLeft < NumberOfChars * 2 || NumberOfChars == 0)
	{
		SerialDevice = ((PGREEN_DEVICE_EXTENSION)DeviceExtension->Green->DeviceExtension)->Serial;
		Irp = IoBuildSynchronousFsdRequest(
			IRP_MJ_WRITE,
			SerialDevice,
			DeviceExtension->SendBuffer, DeviceExtension->SendBufferPosition,
			&ZeroOffset,
			NULL, /* Event */
			&ioStatus);
		if (!Irp)
		{
			DPRINT1("IoBuildSynchronousFsdRequest() failed. Unable to flush output buffer\n");
			return;
		}

		Status = IoCallDriver(SerialDevice, Irp);

		if (!NT_SUCCESS(Status) && Status != STATUS_PENDING)
		{
			DPRINT1("IoCallDriver() failed. Status = 0x%08lx\n", Status);
			return;
		}
		DeviceExtension->SendBufferPosition = 0;
		SizeLeft = sizeof(DeviceExtension->SendBuffer);
	}

	va_start(args, NumberOfChars);
	while (NumberOfChars-- > 0)
	{
		CurrentInt = va_arg(args, int);

		if (CurrentInt > 0)
		{
			CurrentChar = (UCHAR)CurrentInt;

			/* Why 0xff chars are printed on a 'dir' ? */
			if (CurrentChar == 0xff) CurrentChar = ' ';

			DeviceExtension->SendBuffer[DeviceExtension->SendBufferPosition++] = CurrentChar;
			SizeLeft--;
		}
		else if (CurrentInt == 0)
		{
			DeviceExtension->SendBuffer[DeviceExtension->SendBufferPosition++] = '0';
			SizeLeft--;
		}
		else
		{
			CurrentInt = -CurrentInt;
			ASSERT(CurrentInt < 100);
			if (CurrentInt >= 10)
			{
				DeviceExtension->SendBuffer[DeviceExtension->SendBufferPosition++] =
					(CurrentInt / 10) % 10 + '0';
				SizeLeft--;
			}
			DeviceExtension->SendBuffer[DeviceExtension->SendBufferPosition++] =
				CurrentInt % 10 + '0';
			SizeLeft--;
		}
	}
	va_end(args);
}

NTSTATUS
ScreenAddDevice(
	IN PDRIVER_OBJECT DriverObject,
	IN PDEVICE_OBJECT Pdo)
{
	/* We want to be an upper filter of Blue, if it is existing.
	 * We also *have to* create a Fdo on top of the given Pdo.
	 * Hence, we have 2 cases:
	 * - Blue doesn't exist -> Create a unique Fdo (named Blue) at
	 *   the top of the given Pdo
	 * - Blue does exist -> Create a Fdo at the top of the existing
	 *   DO, and create a "pass to Green" FDO at the top of the Pdo
	 */
	PDEVICE_OBJECT Fdo = NULL;
	PDEVICE_OBJECT PassThroughFdo = NULL;
	PDEVICE_OBJECT LowerDevice = NULL;
	PDEVICE_OBJECT PreviousBlue = NULL;
	PSCREEN_DEVICE_EXTENSION DeviceExtension = NULL;
	UNICODE_STRING BlueScreenName = RTL_CONSTANT_STRING(L"\\Device\\BlueScreen");
	NTSTATUS Status;

	DPRINT("ScreenInitialize() called\n");

	/* Try to create a unique Fdo */
	Status = IoCreateDevice(
		DriverObject,
		sizeof(SCREEN_DEVICE_EXTENSION),
		&BlueScreenName,
		FILE_DEVICE_SCREEN,
		FILE_DEVICE_SECURE_OPEN,
		TRUE,
		&Fdo);

	if (Status == STATUS_OBJECT_NAME_COLLISION)
	{
		DPRINT("Attaching to old blue\n");

		/* Suggested by hpoussin .. Hide previous blue device 
		 * This makes us able to coexist with blue, and install
		 * when loaded */
		Status = IoCreateDevice(
			DriverObject,
			sizeof(SCREEN_DEVICE_EXTENSION),
			NULL,
			FILE_DEVICE_SCREEN,
			FILE_DEVICE_SECURE_OPEN,
			TRUE,
			&Fdo);
		if (!NT_SUCCESS(Status))
		{
			DPRINT("IoCreateDevice() failed with status 0x%08lx\n", Status);
			goto cleanup;
		}

		/* Initialize some fields, as IoAttachDevice will trigger the
		 * sending of IRP_MJ_CLEANUP/IRP_MJ_CLOSE. We have to know where to
		 * dispatch these IRPs... */
		((PSCREEN_DEVICE_EXTENSION)Fdo->DeviceExtension)->Common.Type = ScreenPDO;
		Status = IoAttachDevice(
			Fdo,
			&BlueScreenName,
			&LowerDevice);
		if (!NT_SUCCESS(Status))
		{
			DPRINT("IoAttachDevice() failed with status 0x%08lx\n", Status);
			goto cleanup;
		}
		PreviousBlue = LowerDevice;

		/* Attach a faked FDO to PDO */
		Status = IoCreateDevice(
			DriverObject,
			sizeof(COMMON_FDO_DEVICE_EXTENSION),
			NULL,
			FILE_DEVICE_SCREEN,
			FILE_DEVICE_SECURE_OPEN,
			TRUE,
			&PassThroughFdo);
		if (!NT_SUCCESS(Status))
		{
			DPRINT("IoCreateDevice() failed with status 0x%08lx\n", Status);
			goto cleanup;
		}
		((PCOMMON_FDO_DEVICE_EXTENSION)PassThroughFdo->DeviceExtension)->Type = PassThroughFDO;
		((PCOMMON_FDO_DEVICE_EXTENSION)PassThroughFdo->DeviceExtension)->LowerDevice = Fdo;
		PassThroughFdo->StackSize = Fdo->StackSize + 1;
	}
	else if (NT_SUCCESS(Status))
	{
		/* Attach the named Fdo on top of Pdo */
		LowerDevice = IoAttachDeviceToDeviceStack(Fdo, Pdo);
	}
	else
	{
		DPRINT("IoCreateDevice() failed with status 0x%08lx\n", Status);
		return Status;
	}

	/* We definately have a device object. PreviousBlue may or may
	 * not be null */
	DeviceExtension = (PSCREEN_DEVICE_EXTENSION)Fdo->DeviceExtension;
	RtlZeroMemory(DeviceExtension, sizeof(SCREEN_DEVICE_EXTENSION));
	DeviceExtension->Common.Type = ScreenFDO;
	DeviceExtension->Common.LowerDevice = LowerDevice;
	DeviceExtension->Green = ((PGREEN_DRIVER_EXTENSION)IoGetDriverObjectExtension(DriverObject, DriverObject))->GreenMainDO;
	((PGREEN_DEVICE_EXTENSION)DeviceExtension->Green->DeviceExtension)->ScreenFdo = Fdo;
	DeviceExtension->PreviousBlue = PreviousBlue;
	IoAttachDeviceToDeviceStack(PassThroughFdo ? PassThroughFdo : Fdo, Pdo);

	/* initialize screen */
	DeviceExtension->Columns = 80;
	DeviceExtension->Rows = 25;
	DeviceExtension->ScanLines = 16;
	DeviceExtension->VideoMemory = (PUCHAR)ExAllocatePool(
		PagedPool,
		2 * DeviceExtension->Columns * DeviceExtension->Rows * sizeof(UCHAR));
	if (!DeviceExtension->VideoMemory)
	{
		DPRINT("ExAllocatePool() failed\n");
		Status = STATUS_INSUFFICIENT_RESOURCES;
		goto cleanup;
	}
	DeviceExtension->TabWidth = 8;

	/* more initialization */
	DeviceExtension->Mode = ENABLE_PROCESSED_OUTPUT |
		ENABLE_WRAP_AT_EOL_OUTPUT;

	/* initialize screen at next write */
	AddToSendBuffer(DeviceExtension, 2, ESC, 'c'); /* reset device */
	AddToSendBuffer(DeviceExtension, 4, ESC, '[', '7', 'l'); /* disable line wrap */
	AddToSendBuffer(DeviceExtension, 4, ESC, '[', '3', 'g'); /* clear all tabs */

	Fdo->Flags |= DO_POWER_PAGABLE;
	Fdo->Flags &= ~DO_DEVICE_INITIALIZING;

	Status = STATUS_SUCCESS;

cleanup:
	if (!NT_SUCCESS(Status))
	{
		if (DeviceExtension)
			ExFreePool(DeviceExtension->VideoMemory);
		if (LowerDevice)
			IoDetachDevice(LowerDevice);
		if (Fdo)
			IoDeleteDevice(Fdo);
		if (PassThroughFdo)
			IoDeleteDevice(PassThroughFdo);
	}

	return Status;
}

NTSTATUS
ScreenWrite(
	IN PDEVICE_OBJECT DeviceObject,
	IN PIRP Irp)
{
	PIO_STACK_LOCATION Stack;
	PUCHAR Buffer;
	PSCREEN_DEVICE_EXTENSION DeviceExtension;
	PDEVICE_OBJECT SerialDevice;
	PUCHAR VideoMemory; /* FIXME: is it useful? */
	ULONG VideoMemorySize; /* FIXME: is it useful? */

	ULONG Columns, Rows;
	ULONG CursorX, CursorY;
	ULONG i, j;

	DPRINT("ScreenWrite() called\n");

	Stack = IoGetCurrentIrpStackLocation (Irp);
	Buffer = Irp->UserBuffer;
	DeviceExtension = (PSCREEN_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
	VideoMemory = DeviceExtension->VideoMemory;

	SerialDevice = ((PGREEN_DEVICE_EXTENSION)DeviceExtension->Green->DeviceExtension)->Serial;
	if (!SerialDevice)
	{
		DPRINT1("Calling blue\n");
		IoSkipCurrentIrpStackLocation(Irp);
		return IoCallDriver(DeviceExtension->PreviousBlue, Irp);
	}

	Columns = DeviceExtension->Columns;
	Rows = DeviceExtension->Rows;
	CursorX = (DeviceExtension->LogicalOffset / 2) % Columns + 1;
	CursorY = (DeviceExtension->LogicalOffset / 2) / Columns + 1;
	VideoMemorySize = Columns * Rows * 2 * sizeof(UCHAR);

	if (!(DeviceExtension->Mode & ENABLE_PROCESSED_OUTPUT))
	{
		/* raw output mode */
		CHECKPOINT;
		Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
		IoCompleteRequest (Irp, IO_NO_INCREMENT);

		return STATUS_NOT_SUPPORTED;
	}
	else
	{
		for (i = 0; i < Stack->Parameters.Write.Length; i++, Buffer++)
		{
			switch (*Buffer)
			{
				case '\b':
				{
					if (CursorX > 1)
					{
						CursorX--;
						AddToSendBuffer(DeviceExtension, 6, ESC, '[', -(int)CursorY, ';', -(int)CursorX, 'H');
						AddToSendBuffer(DeviceExtension, 1, ' ');
						AddToSendBuffer(DeviceExtension, 6, ESC, '[', -(int)CursorY, ';', -(int)CursorX, 'H');
					}
					else if (CursorY > 1)
					{
						CursorX = Columns;
						CursorY--;
						AddToSendBuffer(DeviceExtension, 6, ESC, '[', -(int)CursorY, ';', -(int)CursorX, 'H');
					}
					break;
				}
				case '\n':
				{
					CursorY++;
					CursorX = 1;
					AddToSendBuffer(DeviceExtension, 1, '\n');
					AddToSendBuffer(DeviceExtension, 6, ESC, '[', -(int)CursorY, ';', '1', 'H');
					break;
				}
				case '\r':
				{
					if (CursorX > 1)
					{
						AddToSendBuffer(DeviceExtension, 4, ESC, '[', -(int)(CursorX-1), 'D');
						CursorX = 1;
					}
					break;
				}
				case '\t':
				{
					ULONG Offset = DeviceExtension->TabWidth - (CursorX % DeviceExtension->TabWidth);
					for (j = 0; j < Offset; j++)
					{
#ifdef FORCE_POSITION
						AddToSendBuffer(DeviceExtension, 6, ESC, '[', -(int)CursorY, ';', -(int)CursorX, 'H');
#endif
						AddToSendBuffer(DeviceExtension, 1, ' ');
						CursorX++;
						if (CursorX > Columns)
						{
							CursorX = 1;
							CursorY++;
						}
					}
					break;
				}
				default:
				{
#ifdef FORCE_POSITION
					AddToSendBuffer(DeviceExtension, 6, ESC, '[', -(int)CursorY, ';', -(int)CursorX, 'H');
#endif
					AddToSendBuffer(DeviceExtension, 1, *Buffer);
					CursorX++;
					if (CursorX > Columns)
					{
						CursorX = 1;
						DPRINT("Y: %lu -> %lu\n", CursorY, CursorY + 1);
						CursorY++;
						AddToSendBuffer(DeviceExtension, 6, ESC, '[', -(int)CursorY, ';', '1', 'H');

					}
				}
			}
			if (CursorY >= Rows)
			{
				DPRINT("Y: %lu -> %lu\n", CursorY, CursorY - 1);
				CursorY--;
				AddToSendBuffer(DeviceExtension, 6, ESC, '[', -(int)1, ';', -(int)(Rows), 'r');
				AddToSendBuffer(DeviceExtension, 2, ESC, 'D');
				AddToSendBuffer(DeviceExtension, 6, ESC, '[', -(int)CursorY, ';', -(int)CursorX, 'H');
			}
		}
	}

	DeviceExtension->LogicalOffset = ((CursorX-1) + (CursorY-1) * Columns) * 2;

	/* flush output buffer */
	AddToSendBuffer(DeviceExtension, 0);

	/* Call lower driver */
	IoSkipCurrentIrpStackLocation(Irp);
	return IoCallDriver(DeviceExtension->Common.LowerDevice, Irp);
}

NTSTATUS
ScreenDeviceControl(
	IN PDEVICE_OBJECT DeviceObject,
	IN PIRP Irp)
{
	PIO_STACK_LOCATION Stack;
	PSCREEN_DEVICE_EXTENSION DeviceExtension;
	PDEVICE_OBJECT SerialDevice;
	NTSTATUS Status;

	Stack = IoGetCurrentIrpStackLocation(Irp);
	DeviceExtension = (PSCREEN_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
	SerialDevice = ((PGREEN_DEVICE_EXTENSION)DeviceExtension->Green->DeviceExtension)->Serial;
	if (!SerialDevice)
	{
		DPRINT1("Calling blue\n");
		IoSkipCurrentIrpStackLocation(Irp);
		return IoCallDriver(DeviceExtension->PreviousBlue, Irp);
	}

	switch (Stack->Parameters.DeviceIoControl.IoControlCode)
	{
#if 0
		case IOCTL_CONSOLE_GET_SCREEN_BUFFER_INFO:
		{
			PCONSOLE_SCREEN_BUFFER_INFO pcsbi;
			DPRINT("IRP_MJ_DEVICE_CONTROL / IOCTL_CONSOLE_GET_SCREEN_BUFFER_INFO\n");

			pcsbi = (PCONSOLE_SCREEN_BUFFER_INFO)Irp->AssociatedIrp.SystemBuffer;

			pcsbi->dwSize.X = DeviceExtension->Columns;
			pcsbi->dwSize.Y = DeviceExtension->Rows;

			pcsbi->dwCursorPosition.X = (SHORT)(DeviceExtension->LogicalOffset % DeviceExtension->Columns);
			pcsbi->dwCursorPosition.Y = (SHORT)(DeviceExtension->LogicalOffset / DeviceExtension->Columns);

			pcsbi->wAttributes = DeviceExtension->CharAttribute;

			pcsbi->srWindow.Left   = 1;
			pcsbi->srWindow.Right  = DeviceExtension->Columns;
			pcsbi->srWindow.Top    = 1;
			pcsbi->srWindow.Bottom = DeviceExtension->Rows;

			pcsbi->dwMaximumWindowSize.X = DeviceExtension->Columns;
			pcsbi->dwMaximumWindowSize.Y = DeviceExtension->Rows;

			Irp->IoStatus.Information = sizeof(CONSOLE_SCREEN_BUFFER_INFO);
			Status = STATUS_SUCCESS;
			break;
		}
		case IOCTL_CONSOLE_SET_SCREEN_BUFFER_INFO:
		{
			PCONSOLE_SCREEN_BUFFER_INFO pcsbi;
			DPRINT("IRP_MJ_DEVICE_CONTROL / IOCTL_CONSOLE_SET_SCREEN_BUFFER_INFO\n");

			pcsbi = (PCONSOLE_SCREEN_BUFFER_INFO)Irp->AssociatedIrp.SystemBuffer;
			/* FIXME: remove */ { pcsbi->dwCursorPosition.X++; }
			/* FIXME: remove */ { pcsbi->dwCursorPosition.Y++; }
			ASSERT(pcsbi->dwCursorPosition.X >= 1);
			ASSERT(pcsbi->dwCursorPosition.Y >= 1);
			ASSERT(pcsbi->dwCursorPosition.X <= DeviceExtension->Columns);
			ASSERT(pcsbi->dwCursorPosition.Y <= DeviceExtension->Rows);

			DeviceExtension->LogicalOffset = (
				(pcsbi->dwCursorPosition.Y-1) * DeviceExtension->Columns +
				(pcsbi->dwCursorPosition.X-1)) * 2;
			AddToSendBuffer(DeviceExtension, 6, ESC, '[',
				-(int)pcsbi->dwCursorPosition.Y, ';',
				-(int)pcsbi->dwCursorPosition.X, 'H');

			/* flush buffer */
			AddToSendBuffer(DeviceExtension, 0);

			DeviceExtension->CharAttribute = pcsbi->wAttributes;

			Irp->IoStatus.Information = 0;
			Status = STATUS_SUCCESS;
			break;
		}
		case IOCTL_CONSOLE_GET_CURSOR_INFO:
		{
			PCONSOLE_CURSOR_INFO pcci = (PCONSOLE_CURSOR_INFO)Irp->AssociatedIrp.SystemBuffer;
			DPRINT("IRP_MJ_DEVICE_CONTROL / IOCTL_CONSOLE_GET_CURSOR_INFO\n");

			pcci->dwSize = 1;
			pcci->bVisible = TRUE;

			Irp->IoStatus.Information = sizeof (CONSOLE_CURSOR_INFO);
			Status = STATUS_SUCCESS;
			break;
		}
		case IOCTL_CONSOLE_GET_MODE:
		{
			PCONSOLE_MODE pcm = (PCONSOLE_MODE)Irp->AssociatedIrp.SystemBuffer;
			DPRINT("IRP_MJ_DEVICE_CONTROL / IOCTL_CONSOLE_GET_MODE\n");

			pcm->dwMode = DeviceExtension->Mode;

			Irp->IoStatus.Information = sizeof(CONSOLE_MODE);
			Status = STATUS_SUCCESS;
			break;
		}
		case IOCTL_CONSOLE_SET_MODE:
		{
			PCONSOLE_MODE pcm = (PCONSOLE_MODE)Irp->AssociatedIrp.SystemBuffer;
			DPRINT("IRP_MJ_DEVICE_CONTROL / IOCTL_CONSOLE_SET_MODE\n");

			DeviceExtension->Mode = pcm->dwMode;

			Irp->IoStatus.Information = 0;
			Status = STATUS_SUCCESS;
			break;
		}
		case IOCTL_CONSOLE_FILL_OUTPUT_ATTRIBUTE:
		{
			DPRINT1("IRP_MJ_DEVICE_CONTROL / IOCTL_CONSOLE_FILL_OUTPUT_ATTRIBUTE\n");
			Status = STATUS_NOT_IMPLEMENTED; /* FIXME: IOCTL_CONSOLE_FILL_OUTPUT_ATTRIBUTE */
			break;
		}
		case IOCTL_CONSOLE_READ_OUTPUT_ATTRIBUTE:
		{
			DPRINT1("IRP_MJ_DEVICE_CONTROL / IOCTL_CONSOLE_READ_OUTPUT_ATTRIBUTE\n");
			Status = STATUS_NOT_IMPLEMENTED; /* FIXME: IOCTL_CONSOLE_READ_OUTPUT_ATTRIBUTE */
			break;
		}
		case IOCTL_CONSOLE_WRITE_OUTPUT_ATTRIBUTE:
		{
			DPRINT1("IRP_MJ_DEVICE_CONTROL / IOCTL_CONSOLE_WRITE_OUTPUT_ATTRIBUTE\n");
			Status = STATUS_NOT_IMPLEMENTED; /* FIXME: IOCTL_CONSOLE_WRITE_OUTPUT_ATTRIBUTE */
			break;
		}
		case IOCTL_CONSOLE_SET_TEXT_ATTRIBUTE:
		{
			DPRINT("IRP_MJ_DEVICE_CONTROL / IOCTL_CONSOLE_SET_TEXT_ATTRIBUTE\n");

			DeviceExtension->CharAttribute = (WORD)*(PWORD)Irp->AssociatedIrp.SystemBuffer;
			Irp->IoStatus.Information = 0;
			Status = STATUS_SUCCESS;
			break;
		}
		case IOCTL_CONSOLE_FILL_OUTPUT_CHARACTER:
		{
			DPRINT1("IRP_MJ_DEVICE_CONTROL / IOCTL_CONSOLE_FILL_OUTPUT_CHARACTER\n");
			Status = STATUS_NOT_IMPLEMENTED; /* FIXME:IOCTL_CONSOLE_FILL_OUTPUT_CHARACTER */
			break;
		}
		case IOCTL_CONSOLE_READ_OUTPUT_CHARACTER:
		{
			DPRINT1("IRP_MJ_DEVICE_CONTROL / IOCTL_CONSOLE_READ_OUTPUT_CHARACTER\n");
			Status = STATUS_NOT_IMPLEMENTED; /* FIXME: IOCTL_CONSOLE_READ_OUTPUT_CHARACTER */
			break;
		}
		case IOCTL_CONSOLE_WRITE_OUTPUT_CHARACTER:
		{
			DPRINT1("IRP_MJ_DEVICE_CONTROL / IOCTL_CONSOLE_WRITE_OUTPUT_CHARACTER\n");
			Status = STATUS_NOT_IMPLEMENTED; /* FIXME: IOCTL_CONSOLE_WRITE_OUTPUT_CHARACTER */
			break;
		}
		case IOCTL_CONSOLE_DRAW:
		{
			PCONSOLE_DRAW ConsoleDraw;
			PUCHAR Video;
			ULONG x, y;
			BOOLEAN DoOptimization = FALSE;
			DPRINT("IRP_MJ_DEVICE_CONTROL / IOCTL_CONSOLE_DRAW\n");

			ConsoleDraw = (PCONSOLE_DRAW)MmGetSystemAddressForMdl(Irp->MdlAddress);
			/* FIXME: remove */ { ConsoleDraw->X++; ConsoleDraw->CursorX++; }
			/* FIXME: remove */ { ConsoleDraw->Y++; ConsoleDraw->CursorY++; }
			DPRINT1("%lu %lu %lu %lu\n",
				ConsoleDraw->X, ConsoleDraw->Y,
				ConsoleDraw->SizeX, ConsoleDraw->SizeY);
			ASSERT(ConsoleDraw->X >= 1);
			ASSERT(ConsoleDraw->Y >= 1);
			ASSERT(ConsoleDraw->X <= DeviceExtension->Columns);
			ASSERT(ConsoleDraw->Y <= DeviceExtension->Rows);
			ASSERT(ConsoleDraw->X + ConsoleDraw->SizeX >= 1);
			ASSERT(ConsoleDraw->Y + ConsoleDraw->SizeY >= 1);
			ASSERT(ConsoleDraw->X + ConsoleDraw->SizeX - 1 <= DeviceExtension->Columns);
			ASSERT(ConsoleDraw->Y + ConsoleDraw->SizeY - 1 <= DeviceExtension->Rows);
			ASSERT(ConsoleDraw->CursorX >= 1);
			ASSERT(ConsoleDraw->CursorY >= 1);
			ASSERT(ConsoleDraw->CursorX <= DeviceExtension->Columns);
			ASSERT(ConsoleDraw->CursorY <= DeviceExtension->Rows);

#if 0
			if (ConsoleDraw->X == 1
				&& ConsoleDraw->Y == 1
				&& ConsoleDraw->SizeX == DeviceExtension->Columns
				&& ConsoleDraw->SizeY == DeviceExtension->Rows)
			{
				CHECKPOINT1;
				/* search if we need to clear all screen */
				DoOptimization = TRUE;
				Video = (PUCHAR)(ConsoleDraw + 1);
				x = 0;
				while (DoOptimization && x < DeviceExtension->Columns * DeviceExtension->Rows)
				{
					if (Video[x++] != ' ')
					{
						CHECKPOINT1;
						DoOptimization = FALSE;
					}
					/*if (Video[x++] != DeviceExtension->CharAttribute) DoOptimization = FALSE; */
				}
				if (DoOptimization)
				{
					CHECKPOINT1;
					AddToSendBuffer(DeviceExtension, 4, ESC, '[', '2', 'J');
				}
			}
#endif
			/* add here more optimizations if needed */

			if (!DoOptimization)
			{
				for (y = 0; y < ConsoleDraw->SizeY; y++)
				{
					AddToSendBuffer(DeviceExtension, 6, ESC, '[',
						-(int)(ConsoleDraw->Y + y), ';',
						-(int)(ConsoleDraw->X), 'H');
					Video = (PUCHAR)(ConsoleDraw + 1);
					Video = &Video[((ConsoleDraw->Y + y) * /*DeviceExtension->Columns +*/ ConsoleDraw->X) * 2];
					for (x = 0; x < ConsoleDraw->SizeX; x++)
					{
						AddToSendBuffer(DeviceExtension, 1, Video[x * 2]);
					}
				}
			}

			DeviceExtension->LogicalOffset = (
				(ConsoleDraw->CursorY-1) * DeviceExtension->Columns +
				(ConsoleDraw->CursorX-1)) * 2;
			AddToSendBuffer(DeviceExtension, 6, ESC, '[',
				-(int)(ConsoleDraw->CursorY), ';',
				-(int)(ConsoleDraw->CursorX), 'H');

			/* flush buffer */
			AddToSendBuffer(DeviceExtension, 0);

			Irp->IoStatus.Information = 0;
			Status = STATUS_SUCCESS;
			break;
		}
#endif
		default:
		{
			DPRINT1("IRP_MJ_DEVICE_CONTROL / unknown ioctl code 0x%lx\n",
				Stack->Parameters.DeviceIoControl.IoControlCode);
			/* Call lower driver */
			IoSkipCurrentIrpStackLocation(Irp);
			return IoCallDriver(DeviceExtension->Common.LowerDevice, Irp);
		}
	}

	if (!NT_SUCCESS(Status))
	{
		/* Don't call blue (if any), as we encountered an error */
		Irp->IoStatus.Status = Status;
		IoCompleteRequest(Irp, IO_NO_INCREMENT);
		return Status;
	}
	else
	{
		/* Call lower driver */
		IoSkipCurrentIrpStackLocation(Irp);
		return IoCallDriver(DeviceExtension->Common.LowerDevice, Irp);
	}
}
