Revert tree-restructure attempt: r66583, r66582, r66581, r66578, sauf ntdll changes...
[reactos.git] / reactos / win32ss / user / winsrv / consrv / frontends / gui / guiterm.c
diff --git a/reactos/win32ss/user/winsrv/consrv/frontends/gui/guiterm.c b/reactos/win32ss/user/winsrv/consrv/frontends/gui/guiterm.c
new file mode 100644 (file)
index 0000000..9e3c0a1
--- /dev/null
@@ -0,0 +1,1121 @@
+/*
+ * COPYRIGHT:       See COPYING in the top level directory
+ * PROJECT:         ReactOS Console Server DLL
+ * FILE:            consrv/frontends/gui/guiterm.c
+ * PURPOSE:         GUI Terminal Front-End
+ * PROGRAMMERS:     Gé van Geldorp
+ *                  Johannes Anderwald
+ *                  Jeffrey Morlan
+ *                  Hermes Belusca-Maito (hermes.belusca@sfr.fr)
+ */
+
+/* INCLUDES *******************************************************************/
+
+#include <consrv.h>
+
+#define NDEBUG
+#include <debug.h>
+
+#include "guiterm.h"
+#include "resource.h"
+
+// HACK!! Remove it when the hack in GuiWriteStream is fixed
+#define CONGUI_UPDATE_TIME    0
+#define CONGUI_UPDATE_TIMER   1
+
+#define PM_CREATE_CONSOLE       (WM_APP + 1)
+#define PM_DESTROY_CONSOLE      (WM_APP + 2)
+
+
+/* GLOBALS ********************************************************************/
+
+typedef struct _GUI_INIT_INFO
+{
+    PCONSOLE_INFO ConsoleInfo;
+    PCONSOLE_START_INFO ConsoleStartInfo;
+    ULONG ProcessId;
+    BOOLEAN IsWindowVisible;
+} GUI_INIT_INFO, *PGUI_INIT_INFO;
+
+static BOOL    ConsInitialized = FALSE;
+static HANDLE  hInputThread = NULL;
+static DWORD   dwInputThreadId = 0;
+
+extern HICON   ghDefaultIcon;
+extern HICON   ghDefaultIconSm;
+extern HCURSOR ghDefaultCursor;
+
+VOID
+SetConWndConsoleLeaderCID(IN PGUI_CONSOLE_DATA GuiData);
+BOOLEAN
+RegisterConWndClass(IN HINSTANCE hInstance);
+BOOLEAN
+UnRegisterConWndClass(HINSTANCE hInstance);
+
+/* FUNCTIONS ******************************************************************/
+
+static VOID
+GetScreenBufferSizeUnits(IN PCONSOLE_SCREEN_BUFFER Buffer,
+                         IN PGUI_CONSOLE_DATA GuiData,
+                         OUT PUINT WidthUnit,
+                         OUT PUINT HeightUnit)
+{
+    if (Buffer == NULL || GuiData == NULL ||
+        WidthUnit == NULL || HeightUnit == NULL)
+    {
+        return;
+    }
+
+    if (GetType(Buffer) == TEXTMODE_BUFFER)
+    {
+        *WidthUnit  = GuiData->CharWidth ;
+        *HeightUnit = GuiData->CharHeight;
+    }
+    else /* if (GetType(Buffer) == GRAPHICS_BUFFER) */
+    {
+        *WidthUnit  = 1;
+        *HeightUnit = 1;
+    }
+}
+
+VOID
+GuiConsoleMoveWindow(PGUI_CONSOLE_DATA GuiData)
+{
+    /* Move the window if needed (not positioned by the system) */
+    if (!GuiData->GuiInfo.AutoPosition)
+    {
+        SetWindowPos(GuiData->hWindow,
+                     NULL,
+                     GuiData->GuiInfo.WindowOrigin.x,
+                     GuiData->GuiInfo.WindowOrigin.y,
+                     0, 0,
+                     SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
+    }
+}
+
+static VOID
+SmallRectToRect(PGUI_CONSOLE_DATA GuiData, PRECT Rect, PSMALL_RECT SmallRect)
+{
+    PCONSOLE_SCREEN_BUFFER Buffer = GuiData->ActiveBuffer;
+    UINT WidthUnit, HeightUnit;
+
+    GetScreenBufferSizeUnits(Buffer, GuiData, &WidthUnit, &HeightUnit);
+
+    Rect->left   = (SmallRect->Left       - Buffer->ViewOrigin.X) * WidthUnit ;
+    Rect->top    = (SmallRect->Top        - Buffer->ViewOrigin.Y) * HeightUnit;
+    Rect->right  = (SmallRect->Right  + 1 - Buffer->ViewOrigin.X) * WidthUnit ;
+    Rect->bottom = (SmallRect->Bottom + 1 - Buffer->ViewOrigin.Y) * HeightUnit;
+}
+
+static VOID
+DrawRegion(PGUI_CONSOLE_DATA GuiData,
+           SMALL_RECT* Region)
+{
+    RECT RegionRect;
+
+    SmallRectToRect(GuiData, &RegionRect, Region);
+    /* Do not erase the background: it speeds up redrawing and reduce flickering */
+    InvalidateRect(GuiData->hWindow, &RegionRect, FALSE);
+    /**UpdateWindow(GuiData->hWindow);**/
+}
+
+VOID
+InvalidateCell(PGUI_CONSOLE_DATA GuiData,
+               SHORT x, SHORT y)
+{
+    SMALL_RECT CellRect = { x, y, x, y };
+    DrawRegion(GuiData, &CellRect);
+}
+
+
+/******************************************************************************
+ *                        GUI Terminal Initialization                         *
+ ******************************************************************************/
+
+VOID
+SwitchFullScreen(PGUI_CONSOLE_DATA GuiData, BOOL FullScreen);
+VOID
+CreateSysMenu(HWND hWnd);
+
+static DWORD NTAPI
+GuiConsoleInputThread(PVOID Param)
+{
+    PHANDLE GraphicsStartupEvent = (PHANDLE)Param;
+    LONG WindowCount = 0;
+    MSG msg;
+
+    /*
+     * This thread dispatches all the console notifications to the
+     * notification window. It is common for all the console windows.
+     */
+
+    /* The thread has been initialized, set the event */
+    NtSetEvent(*GraphicsStartupEvent, NULL);
+
+    while (GetMessageW(&msg, NULL, 0, 0))
+    {
+        switch (msg.message)
+        {
+            case PM_CREATE_CONSOLE:
+            {
+                PGUI_CONSOLE_DATA GuiData = (PGUI_CONSOLE_DATA)msg.lParam;
+                PCONSRV_CONSOLE Console = GuiData->Console;
+                HWND NewWindow;
+                RECT rcWnd;
+
+                DPRINT("PM_CREATE_CONSOLE -- creating window\n");
+
+                NewWindow = CreateWindowExW(WS_EX_CLIENTEDGE,
+                                            GUI_CONWND_CLASS,
+                                            Console->Title.Buffer,
+                                            WS_OVERLAPPEDWINDOW | WS_HSCROLL | WS_VSCROLL,
+                                            CW_USEDEFAULT,
+                                            CW_USEDEFAULT,
+                                            CW_USEDEFAULT,
+                                            CW_USEDEFAULT,
+                                            GuiData->IsWindowVisible ? HWND_DESKTOP : HWND_MESSAGE,
+                                            NULL,
+                                            ConSrvDllInstance,
+                                            (PVOID)GuiData);
+                if (NewWindow == NULL)
+                {
+                    DPRINT1("Failed to create a new console window\n");
+                    continue;
+                }
+
+                ASSERT(NewWindow == GuiData->hWindow);
+
+                InterlockedIncrement(&WindowCount);
+
+                //
+                // FIXME: TODO: Move everything there into conwnd.c!OnNcCreate()
+                //
+
+                /* Retrieve our real position */
+                // See conwnd.c!OnMove()
+                GetWindowRect(GuiData->hWindow, &rcWnd);
+                GuiData->GuiInfo.WindowOrigin.x = rcWnd.left;
+                GuiData->GuiInfo.WindowOrigin.y = rcWnd.top;
+
+                if (GuiData->IsWindowVisible)
+                {
+                    /* Move and resize the window to the user's values */
+                    /* CAN WE DEADLOCK ?? */
+                    GuiConsoleMoveWindow(GuiData); // FIXME: This MUST be done via the CreateWindowExW call.
+                    SendMessageW(GuiData->hWindow, PM_RESIZE_TERMINAL, 0, 0);
+                }
+
+                // FIXME: HACK: Potential HACK for CORE-8129; see revision 63595.
+                CreateSysMenu(GuiData->hWindow);
+
+                if (GuiData->IsWindowVisible)
+                {
+                    /* Switch to full-screen mode if necessary */
+                    // FIXME: Move elsewhere, it cause misdrawings of the window.
+                    if (GuiData->GuiInfo.FullScreen) SwitchFullScreen(GuiData, TRUE);
+
+                    DPRINT("PM_CREATE_CONSOLE -- showing window\n");
+                    // ShowWindow(NewWindow, (int)GuiData->GuiInfo.ShowWindow);
+                    ShowWindowAsync(NewWindow, (int)GuiData->GuiInfo.ShowWindow);
+                    DPRINT("Window showed\n");
+                }
+                else
+                {
+                    DPRINT("PM_CREATE_CONSOLE -- hidden window\n");
+                    ShowWindowAsync(NewWindow, SW_HIDE);
+                }
+
+                continue;
+            }
+
+            case PM_DESTROY_CONSOLE:
+            {
+                PGUI_CONSOLE_DATA GuiData = (PGUI_CONSOLE_DATA)msg.lParam;
+                MSG TempMsg;
+
+                /* Exit the full screen mode if it was already set */
+                // LeaveFullScreen(GuiData);
+
+                /*
+                 * Window creation is done using a PostMessage(), so it's possible
+                 * that the window that we want to destroy doesn't exist yet.
+                 * So first empty the message queue.
+                 */
+                /*
+                while (PeekMessageW(&TempMsg, NULL, 0, 0, PM_REMOVE))
+                {
+                    TranslateMessage(&TempMsg);
+                    DispatchMessageW(&TempMsg);
+                }*/
+                while (PeekMessageW(&TempMsg, NULL, 0, 0, PM_REMOVE)) ;
+
+                if (GuiData->hWindow == NULL) continue;
+
+                DestroyWindow(GuiData->hWindow);
+
+                NtSetEvent(GuiData->hGuiTermEvent, NULL);
+
+                if (InterlockedDecrement(&WindowCount) == 0)
+                {
+                    DPRINT("CONSRV: Going to quit the Input Thread!!\n");
+                    goto Quit;
+                }
+
+                continue;
+            }
+        }
+
+        TranslateMessage(&msg);
+        DispatchMessageW(&msg);
+    }
+
+Quit:
+    DPRINT("CONSRV: Quit the Input Thread!!\n");
+
+    hInputThread = NULL;
+    dwInputThreadId = 0;
+
+    return 1;
+}
+
+static BOOL
+GuiInit(VOID)
+{
+    /* Exit if we were already initialized */
+    // if (ConsInitialized) return TRUE;
+
+    /*
+     * Initialize and register the console window class, if needed.
+     */
+    if (!ConsInitialized)
+    {
+        if (!RegisterConWndClass(ConSrvDllInstance)) return FALSE;
+        ConsInitialized = TRUE;
+    }
+
+    /*
+     * Set-up the console input thread
+     */
+    if (hInputThread == NULL)
+    {
+        HANDLE GraphicsStartupEvent;
+        NTSTATUS Status;
+
+        Status = NtCreateEvent(&GraphicsStartupEvent, EVENT_ALL_ACCESS,
+                               NULL, SynchronizationEvent, FALSE);
+        if (!NT_SUCCESS(Status)) return FALSE;
+
+        hInputThread = CreateThread(NULL,
+                                    0,
+                                    GuiConsoleInputThread,
+                                    (PVOID)&GraphicsStartupEvent,
+                                    0,
+                                    &dwInputThreadId);
+        if (hInputThread == NULL)
+        {
+            NtClose(GraphicsStartupEvent);
+            DPRINT1("CONSRV: Failed to create graphics console thread.\n");
+            return FALSE;
+        }
+        SetThreadPriority(hInputThread, THREAD_PRIORITY_HIGHEST);
+        CloseHandle(hInputThread);
+
+        WaitForSingleObject(GraphicsStartupEvent, INFINITE);
+        NtClose(GraphicsStartupEvent);
+    }
+
+    // ConsInitialized = TRUE;
+
+    return TRUE;
+}
+
+
+/******************************************************************************
+ *                             GUI Console Driver                             *
+ ******************************************************************************/
+
+static VOID NTAPI
+GuiDeinitFrontEnd(IN OUT PFRONTEND This);
+
+static NTSTATUS NTAPI
+GuiInitFrontEnd(IN OUT PFRONTEND This,
+                IN PCONSRV_CONSOLE Console)
+{
+    PGUI_INIT_INFO GuiInitInfo;
+    PCONSOLE_INFO  ConsoleInfo;
+    PCONSOLE_START_INFO ConsoleStartInfo;
+
+    PGUI_CONSOLE_DATA GuiData;
+    GUI_CONSOLE_INFO  TermInfo;
+
+    if (This == NULL || Console == NULL || This->Context2 == NULL)
+        return STATUS_INVALID_PARAMETER;
+
+    ASSERT(This->Console == Console);
+
+    GuiInitInfo = This->Context2;
+
+    if (GuiInitInfo->ConsoleInfo == NULL || GuiInitInfo->ConsoleStartInfo == NULL)
+        return STATUS_INVALID_PARAMETER;
+
+    ConsoleInfo      = GuiInitInfo->ConsoleInfo;
+    ConsoleStartInfo = GuiInitInfo->ConsoleStartInfo;
+
+    /* Terminal data allocation */
+    GuiData = ConsoleAllocHeap(HEAP_ZERO_MEMORY, sizeof(GUI_CONSOLE_DATA));
+    if (!GuiData)
+    {
+        DPRINT1("CONSRV: Failed to create GUI_CONSOLE_DATA\n");
+        return STATUS_UNSUCCESSFUL;
+    }
+    ///// /* HACK */ Console->FrontEndIFace.Context = (PVOID)GuiData; /* HACK */
+    GuiData->Console      = Console;
+    GuiData->ActiveBuffer = Console->ActiveBuffer;
+    GuiData->hWindow = NULL;
+    GuiData->IsWindowVisible = GuiInitInfo->IsWindowVisible;
+
+    /* The console can be resized */
+    Console->FixedSize = FALSE;
+
+    InitializeCriticalSection(&GuiData->Lock);
+
+
+    /*
+     * Load terminal settings
+     */
+
+    /* 1. Load the default settings */
+    GuiConsoleGetDefaultSettings(&TermInfo, GuiInitInfo->ProcessId);
+
+    if (GuiData->IsWindowVisible)
+    {
+        /* 2. Load the remaining console settings via the registry */
+        if ((ConsoleStartInfo->dwStartupFlags & STARTF_TITLEISLINKNAME) == 0)
+        {
+            /* Load the terminal infos from the registry */
+            GuiConsoleReadUserSettings(&TermInfo,
+                                       ConsoleInfo->ConsoleTitle,
+                                       GuiInitInfo->ProcessId);
+
+            /*
+             * Now, update them with the properties the user might gave to us
+             * via the STARTUPINFO structure before calling CreateProcess
+             * (and which was transmitted via the ConsoleStartInfo structure).
+             * We therefore overwrite the values read in the registry.
+             */
+            if (ConsoleStartInfo->dwStartupFlags & STARTF_USESHOWWINDOW)
+            {
+                TermInfo.ShowWindow = ConsoleStartInfo->wShowWindow;
+            }
+            if (ConsoleStartInfo->dwStartupFlags & STARTF_USEPOSITION)
+            {
+                TermInfo.AutoPosition = FALSE;
+                TermInfo.WindowOrigin.x = ConsoleStartInfo->dwWindowOrigin.X;
+                TermInfo.WindowOrigin.y = ConsoleStartInfo->dwWindowOrigin.Y;
+            }
+            if (ConsoleStartInfo->dwStartupFlags & STARTF_RUNFULLSCREEN)
+            {
+                TermInfo.FullScreen = TRUE;
+            }
+        }
+    }
+
+
+    /*
+     * Set up GUI data
+     */
+
+    // Font data
+    wcsncpy(GuiData->GuiInfo.FaceName, TermInfo.FaceName, LF_FACESIZE);
+    GuiData->GuiInfo.FaceName[LF_FACESIZE - 1] = UNICODE_NULL;
+    GuiData->GuiInfo.FontFamily     = TermInfo.FontFamily;
+    GuiData->GuiInfo.FontSize       = TermInfo.FontSize;
+    GuiData->GuiInfo.FontWeight     = TermInfo.FontWeight;
+
+    // Display
+    GuiData->GuiInfo.FullScreen     = TermInfo.FullScreen;
+    GuiData->GuiInfo.ShowWindow     = TermInfo.ShowWindow;
+    GuiData->GuiInfo.AutoPosition   = TermInfo.AutoPosition;
+    GuiData->GuiInfo.WindowOrigin   = TermInfo.WindowOrigin;
+
+    /* Initialize the icon handles */
+    if (ConsoleStartInfo->hIcon != NULL)
+        GuiData->hIcon = ConsoleStartInfo->hIcon;
+    else
+        GuiData->hIcon = ghDefaultIcon;
+
+    if (ConsoleStartInfo->hIconSm != NULL)
+        GuiData->hIconSm = ConsoleStartInfo->hIconSm;
+    else
+        GuiData->hIconSm = ghDefaultIconSm;
+
+    ASSERT(GuiData->hIcon && GuiData->hIconSm);
+
+    /* Mouse is shown by default with its default cursor shape */
+    GuiData->hCursor = ghDefaultCursor;
+    GuiData->MouseCursorRefCount = 0;
+
+    /* A priori don't ignore mouse signals */
+    GuiData->IgnoreNextMouseSignal = FALSE;
+
+    /* Close button and the corresponding system menu item are enabled by default */
+    GuiData->IsCloseButtonEnabled = TRUE;
+
+    /* There is no user-reserved menu id range by default */
+    GuiData->CmdIdLow = GuiData->CmdIdHigh = 0;
+
+    /* Initialize the selection */
+    RtlZeroMemory(&GuiData->Selection, sizeof(GuiData->Selection));
+    GuiData->Selection.dwFlags = CONSOLE_NO_SELECTION;
+    RtlZeroMemory(&GuiData->dwSelectionCursor, sizeof(GuiData->dwSelectionCursor));
+    GuiData->LineSelection = FALSE; // Default to block selection
+    // TODO: Retrieve the selection mode via the registry.
+
+    /* Finally, finish to initialize the frontend structure */
+    This->Context  = GuiData;
+    if (This->Context2) ConsoleFreeHeap(This->Context2);
+    This->Context2 = NULL;
+
+    /*
+     * We need to wait until the GUI has been fully initialized
+     * to retrieve custom settings i.e. WindowSize etc...
+     * Ideally we could use SendNotifyMessage for this but its not
+     * yet implemented.
+     */
+    NtCreateEvent(&GuiData->hGuiInitEvent, EVENT_ALL_ACCESS,
+                  NULL, SynchronizationEvent, FALSE);
+    NtCreateEvent(&GuiData->hGuiTermEvent, EVENT_ALL_ACCESS,
+                  NULL, SynchronizationEvent, FALSE);
+
+    DPRINT("GUI - Checkpoint\n");
+
+    /* Create the terminal window */
+    PostThreadMessageW(dwInputThreadId, PM_CREATE_CONSOLE, 0, (LPARAM)GuiData);
+
+    /* Wait until initialization has finished */
+    WaitForSingleObject(GuiData->hGuiInitEvent, INFINITE);
+    DPRINT("OK we created the console window\n");
+    NtClose(GuiData->hGuiInitEvent);
+    GuiData->hGuiInitEvent = NULL;
+
+    /* Check whether we really succeeded in initializing the terminal window */
+    if (GuiData->hWindow == NULL)
+    {
+        DPRINT("GuiInitConsole - We failed at creating a new terminal window\n");
+        GuiDeinitFrontEnd(This);
+        return STATUS_UNSUCCESSFUL;
+    }
+
+    return STATUS_SUCCESS;
+}
+
+static VOID NTAPI
+GuiDeinitFrontEnd(IN OUT PFRONTEND This)
+{
+    PGUI_CONSOLE_DATA GuiData = This->Context;
+
+    DPRINT("Send PM_DESTROY_CONSOLE message and wait on hGuiTermEvent...\n");
+    PostThreadMessageW(dwInputThreadId, PM_DESTROY_CONSOLE, 0, (LPARAM)GuiData);
+    WaitForSingleObject(GuiData->hGuiTermEvent, INFINITE);
+    DPRINT("hGuiTermEvent set\n");
+    NtClose(GuiData->hGuiTermEvent);
+    GuiData->hGuiTermEvent = NULL;
+
+    DPRINT("Destroying icons !! - GuiData->hIcon = 0x%p ; ghDefaultIcon = 0x%p ; GuiData->hIconSm = 0x%p ; ghDefaultIconSm = 0x%p\n",
+            GuiData->hIcon, ghDefaultIcon, GuiData->hIconSm, ghDefaultIconSm);
+    if (GuiData->hIcon != NULL && GuiData->hIcon != ghDefaultIcon)
+    {
+        DPRINT("Destroy hIcon\n");
+        DestroyIcon(GuiData->hIcon);
+    }
+    if (GuiData->hIconSm != NULL && GuiData->hIconSm != ghDefaultIconSm)
+    {
+        DPRINT("Destroy hIconSm\n");
+        DestroyIcon(GuiData->hIconSm);
+    }
+
+    This->Context = NULL;
+    DeleteCriticalSection(&GuiData->Lock);
+    ConsoleFreeHeap(GuiData);
+
+    DPRINT("Quit GuiDeinitFrontEnd\n");
+}
+
+static VOID NTAPI
+GuiDrawRegion(IN OUT PFRONTEND This,
+              SMALL_RECT* Region)
+{
+    PGUI_CONSOLE_DATA GuiData = This->Context;
+
+    /* Do nothing if the window is hidden */
+    if (!GuiData->IsWindowVisible) return;
+
+    DrawRegion(GuiData, Region);
+}
+
+static VOID NTAPI
+GuiWriteStream(IN OUT PFRONTEND This,
+               SMALL_RECT* Region,
+               SHORT CursorStartX,
+               SHORT CursorStartY,
+               UINT ScrolledLines,
+               PWCHAR Buffer,
+               UINT Length)
+{
+    PGUI_CONSOLE_DATA GuiData = This->Context;
+    PCONSOLE_SCREEN_BUFFER Buff;
+    SHORT CursorEndX, CursorEndY;
+    RECT ScrollRect;
+
+    if (NULL == GuiData || NULL == GuiData->hWindow) return;
+
+    /* Do nothing if the window is hidden */
+    if (!GuiData->IsWindowVisible) return;
+
+    Buff = GuiData->ActiveBuffer;
+    if (GetType(Buff) != TEXTMODE_BUFFER) return;
+
+    if (0 != ScrolledLines)
+    {
+        ScrollRect.left = 0;
+        ScrollRect.top = 0;
+        ScrollRect.right = Buff->ViewSize.X * GuiData->CharWidth;
+        ScrollRect.bottom = Region->Top * GuiData->CharHeight;
+
+        ScrollWindowEx(GuiData->hWindow,
+                       0,
+                       -(int)(ScrolledLines * GuiData->CharHeight),
+                       &ScrollRect,
+                       NULL,
+                       NULL,
+                       NULL,
+                       SW_INVALIDATE);
+    }
+
+    DrawRegion(GuiData, Region);
+
+    if (CursorStartX < Region->Left || Region->Right < CursorStartX
+            || CursorStartY < Region->Top || Region->Bottom < CursorStartY)
+    {
+        InvalidateCell(GuiData, CursorStartX, CursorStartY);
+    }
+
+    CursorEndX = Buff->CursorPosition.X;
+    CursorEndY = Buff->CursorPosition.Y;
+    if ((CursorEndX < Region->Left || Region->Right < CursorEndX
+            || CursorEndY < Region->Top || Region->Bottom < CursorEndY)
+            && (CursorEndX != CursorStartX || CursorEndY != CursorStartY))
+    {
+        InvalidateCell(GuiData, CursorEndX, CursorEndY);
+    }
+
+    // HACK!!
+    // Set up the update timer (very short interval) - this is a "hack" for getting the OS to
+    // repaint the window without having it just freeze up and stay on the screen permanently.
+    Buff->CursorBlinkOn = TRUE;
+    SetTimer(GuiData->hWindow, CONGUI_UPDATE_TIMER, CONGUI_UPDATE_TIME, NULL);
+}
+
+/* static */ VOID NTAPI
+GuiRingBell(IN OUT PFRONTEND This)
+{
+    PGUI_CONSOLE_DATA GuiData = This->Context;
+
+    /* Emit an error beep sound */
+    SendNotifyMessage(GuiData->hWindow, PM_CONSOLE_BEEP, 0, 0);
+}
+
+static BOOL NTAPI
+GuiSetCursorInfo(IN OUT PFRONTEND This,
+                 PCONSOLE_SCREEN_BUFFER Buff)
+{
+    PGUI_CONSOLE_DATA GuiData = This->Context;
+
+    /* Do nothing if the window is hidden */
+    if (!GuiData->IsWindowVisible) return TRUE;
+
+    if (GuiData->ActiveBuffer == Buff)
+    {
+        InvalidateCell(GuiData, Buff->CursorPosition.X, Buff->CursorPosition.Y);
+    }
+
+    return TRUE;
+}
+
+static BOOL NTAPI
+GuiSetScreenInfo(IN OUT PFRONTEND This,
+                 PCONSOLE_SCREEN_BUFFER Buff,
+                 SHORT OldCursorX,
+                 SHORT OldCursorY)
+{
+    PGUI_CONSOLE_DATA GuiData = This->Context;
+
+    /* Do nothing if the window is hidden */
+    if (!GuiData->IsWindowVisible) return TRUE;
+
+    if (GuiData->ActiveBuffer == Buff)
+    {
+        /* Redraw char at old position (remove cursor) */
+        InvalidateCell(GuiData, OldCursorX, OldCursorY);
+        /* Redraw char at new position (show cursor) */
+        InvalidateCell(GuiData, Buff->CursorPosition.X, Buff->CursorPosition.Y);
+    }
+
+    return TRUE;
+}
+
+static VOID NTAPI
+GuiResizeTerminal(IN OUT PFRONTEND This)
+{
+    PGUI_CONSOLE_DATA GuiData = This->Context;
+
+    /* Resize the window to the user's values */
+    PostMessageW(GuiData->hWindow, PM_RESIZE_TERMINAL, 0, 0);
+}
+
+static VOID NTAPI
+GuiSetActiveScreenBuffer(IN OUT PFRONTEND This)
+{
+    PGUI_CONSOLE_DATA GuiData = This->Context;
+    PCONSOLE_SCREEN_BUFFER ActiveBuffer;
+    HPALETTE hPalette;
+
+    EnterCriticalSection(&GuiData->Lock);
+    GuiData->WindowSizeLock = TRUE;
+
+    InterlockedExchangePointer(&GuiData->ActiveBuffer,
+                               ConDrvGetActiveScreenBuffer(GuiData->Console));
+
+    GuiData->WindowSizeLock = FALSE;
+    LeaveCriticalSection(&GuiData->Lock);
+
+    ActiveBuffer = GuiData->ActiveBuffer;
+
+    /* Change the current palette */
+    if (ActiveBuffer->PaletteHandle == NULL)
+    {
+        hPalette = GuiData->hSysPalette;
+    }
+    else
+    {
+        hPalette = ActiveBuffer->PaletteHandle;
+    }
+
+    DPRINT("GuiSetActiveScreenBuffer using palette 0x%p\n", hPalette);
+
+    /* Set the new palette for the framebuffer */
+    SelectPalette(GuiData->hMemDC, hPalette, FALSE);
+
+    /* Specify the use of the system palette for the framebuffer */
+    SetSystemPaletteUse(GuiData->hMemDC, ActiveBuffer->PaletteUsage);
+
+    /* Realize the (logical) palette */
+    RealizePalette(GuiData->hMemDC);
+
+    GuiResizeTerminal(This);
+    // ConioDrawConsole(Console);
+}
+
+static VOID NTAPI
+GuiReleaseScreenBuffer(IN OUT PFRONTEND This,
+                       IN PCONSOLE_SCREEN_BUFFER ScreenBuffer)
+{
+    PGUI_CONSOLE_DATA GuiData = This->Context;
+
+    /*
+     * If we were notified to release a screen buffer that is not actually
+     * ours, then just ignore the notification...
+     */
+    if (ScreenBuffer != GuiData->ActiveBuffer) return;
+
+    /*
+     * ... else, we must release our active buffer. Two cases are present:
+     * - If ScreenBuffer (== GuiData->ActiveBuffer) IS NOT the console
+     *   active screen buffer, then we can safely switch to it.
+     * - If ScreenBuffer IS the console active screen buffer, we must release
+     *   it ONLY.
+     */
+
+    /* Release the old active palette and set the default one */
+    if (GetCurrentObject(GuiData->hMemDC, OBJ_PAL) == ScreenBuffer->PaletteHandle)
+    {
+        /* Set the new palette */
+        SelectPalette(GuiData->hMemDC, GuiData->hSysPalette, FALSE);
+    }
+
+    /* Set the adequate active screen buffer */
+    if (ScreenBuffer != GuiData->Console->ActiveBuffer)
+    {
+        GuiSetActiveScreenBuffer(This);
+    }
+    else
+    {
+        EnterCriticalSection(&GuiData->Lock);
+        GuiData->WindowSizeLock = TRUE;
+
+        InterlockedExchangePointer(&GuiData->ActiveBuffer, NULL);
+
+        GuiData->WindowSizeLock = FALSE;
+        LeaveCriticalSection(&GuiData->Lock);
+    }
+}
+
+static BOOL NTAPI
+GuiSetMouseCursor(IN OUT PFRONTEND This,
+                  HCURSOR CursorHandle);
+
+static VOID NTAPI
+GuiRefreshInternalInfo(IN OUT PFRONTEND This)
+{
+    PGUI_CONSOLE_DATA GuiData = This->Context;
+
+    /* Update the console leader information held by the window */
+    SetConWndConsoleLeaderCID(GuiData);
+
+    /*
+     * HACK:
+     * We reset the cursor here so that, when a console app quits, we reset
+     * the cursor to the default one. It's quite a hack since it doesn't proceed
+     * per - console process... This must be fixed.
+     *
+     * See GuiInitConsole(...) for more information.
+     */
+
+    /* Mouse is shown by default with its default cursor shape */
+    GuiData->MouseCursorRefCount = 0; // Reinitialize the reference counter
+    GuiSetMouseCursor(This, NULL);
+}
+
+static VOID NTAPI
+GuiChangeTitle(IN OUT PFRONTEND This)
+{
+    PGUI_CONSOLE_DATA GuiData = This->Context;
+    // PostMessageW(GuiData->hWindow, PM_CONSOLE_SET_TITLE, 0, 0);
+    SetWindowTextW(GuiData->hWindow, GuiData->Console->Title.Buffer);
+}
+
+static BOOL NTAPI
+GuiChangeIcon(IN OUT PFRONTEND This,
+              HICON IconHandle)
+{
+    PGUI_CONSOLE_DATA GuiData = This->Context;
+    HICON hIcon, hIconSm;
+
+    if (IconHandle == NULL)
+    {
+        hIcon   = ghDefaultIcon;
+        hIconSm = ghDefaultIconSm;
+    }
+    else
+    {
+        hIcon   = CopyIcon(IconHandle);
+        hIconSm = CopyIcon(IconHandle);
+    }
+
+    if (hIcon == NULL)
+    {
+        return FALSE;
+    }
+
+    if (hIcon != GuiData->hIcon)
+    {
+        if (GuiData->hIcon != NULL && GuiData->hIcon != ghDefaultIcon)
+        {
+            DestroyIcon(GuiData->hIcon);
+        }
+        if (GuiData->hIconSm != NULL && GuiData->hIconSm != ghDefaultIconSm)
+        {
+            DestroyIcon(GuiData->hIconSm);
+        }
+
+        GuiData->hIcon   = hIcon;
+        GuiData->hIconSm = hIconSm;
+
+        DPRINT("Set icons in GuiChangeIcon\n");
+        PostMessageW(GuiData->hWindow, WM_SETICON, ICON_BIG  , (LPARAM)GuiData->hIcon  );
+        PostMessageW(GuiData->hWindow, WM_SETICON, ICON_SMALL, (LPARAM)GuiData->hIconSm);
+    }
+
+    return TRUE;
+}
+
+static HWND NTAPI
+GuiGetConsoleWindowHandle(IN OUT PFRONTEND This)
+{
+    PGUI_CONSOLE_DATA GuiData = This->Context;
+    return GuiData->hWindow;
+}
+
+static VOID NTAPI
+GuiGetLargestConsoleWindowSize(IN OUT PFRONTEND This,
+                               PCOORD pSize)
+{
+    PGUI_CONSOLE_DATA GuiData = This->Context;
+    PCONSOLE_SCREEN_BUFFER ActiveBuffer;
+    RECT WorkArea;
+    LONG width, height;
+    UINT WidthUnit, HeightUnit;
+
+    if (!pSize) return;
+
+    if (!SystemParametersInfoW(SPI_GETWORKAREA, 0, &WorkArea, 0))
+    {
+        DPRINT1("SystemParametersInfoW failed - What to do ??\n");
+        return;
+    }
+
+    ActiveBuffer = GuiData->ActiveBuffer;
+    if (ActiveBuffer)
+    {
+        GetScreenBufferSizeUnits(ActiveBuffer, GuiData, &WidthUnit, &HeightUnit);
+    }
+    else
+    {
+        /* Default: text mode */
+        WidthUnit  = GuiData->CharWidth ;
+        HeightUnit = GuiData->CharHeight;
+    }
+
+    width  = WorkArea.right;
+    height = WorkArea.bottom;
+
+    width  -= (2 * (GetSystemMetrics(SM_CXFRAME) + GetSystemMetrics(SM_CXEDGE)));
+    height -= (2 * (GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYEDGE)) + GetSystemMetrics(SM_CYCAPTION));
+
+    if (width  < 0) width  = 0;
+    if (height < 0) height = 0;
+
+    pSize->X = (SHORT)(width  / (int)WidthUnit ) /* HACK */ + 2;
+    pSize->Y = (SHORT)(height / (int)HeightUnit) /* HACK */ + 1;
+}
+
+static BOOL NTAPI
+GuiGetSelectionInfo(IN OUT PFRONTEND This,
+                    PCONSOLE_SELECTION_INFO pSelectionInfo)
+{
+    PGUI_CONSOLE_DATA GuiData = This->Context;
+
+    if (pSelectionInfo == NULL) return FALSE;
+
+    ZeroMemory(pSelectionInfo, sizeof(CONSOLE_SELECTION_INFO));
+    if (GuiData->Selection.dwFlags != CONSOLE_NO_SELECTION)
+        RtlCopyMemory(pSelectionInfo, &GuiData->Selection, sizeof(CONSOLE_SELECTION_INFO));
+
+    return TRUE;
+}
+
+static BOOL NTAPI
+GuiSetPalette(IN OUT PFRONTEND This,
+              HPALETTE PaletteHandle,
+              UINT PaletteUsage)
+{
+    PGUI_CONSOLE_DATA GuiData = This->Context;
+    HPALETTE OldPalette;
+
+    // if (GetType(GuiData->ActiveBuffer) != GRAPHICS_BUFFER) return FALSE;
+    if (PaletteHandle == NULL) return FALSE;
+
+    /* Set the new palette for the framebuffer */
+    OldPalette = SelectPalette(GuiData->hMemDC, PaletteHandle, FALSE);
+    if (OldPalette == NULL) return FALSE;
+
+    /* Specify the use of the system palette for the framebuffer */
+    SetSystemPaletteUse(GuiData->hMemDC, PaletteUsage);
+
+    /* Realize the (logical) palette */
+    RealizePalette(GuiData->hMemDC);
+
+    /* Save the original system palette handle */
+    if (GuiData->hSysPalette == NULL) GuiData->hSysPalette = OldPalette;
+
+    return TRUE;
+}
+
+static ULONG NTAPI
+GuiGetDisplayMode(IN OUT PFRONTEND This)
+{
+    PGUI_CONSOLE_DATA GuiData = This->Context;
+    ULONG DisplayMode = 0;
+
+    if (GuiData->GuiInfo.FullScreen)
+        DisplayMode |= CONSOLE_FULLSCREEN_HARDWARE; // CONSOLE_FULLSCREEN
+    else
+        DisplayMode |= CONSOLE_WINDOWED;
+
+    return DisplayMode;
+}
+
+static BOOL NTAPI
+GuiSetDisplayMode(IN OUT PFRONTEND This,
+                  ULONG NewMode)
+{
+    PGUI_CONSOLE_DATA GuiData = This->Context;
+    BOOL FullScreen;
+
+    if (NewMode & ~(CONSOLE_FULLSCREEN_MODE | CONSOLE_WINDOWED_MODE))
+        return FALSE;
+
+    /* Do nothing if the window is hidden */
+    if (!GuiData->IsWindowVisible) return TRUE;
+
+    FullScreen = ((NewMode & CONSOLE_FULLSCREEN_MODE) != 0);
+
+    if (FullScreen != GuiData->GuiInfo.FullScreen)
+    {
+        SwitchFullScreen(GuiData, FullScreen);
+    }
+
+    return TRUE;
+}
+
+static INT NTAPI
+GuiShowMouseCursor(IN OUT PFRONTEND This,
+                   BOOL Show)
+{
+    PGUI_CONSOLE_DATA GuiData = This->Context;
+
+    if (GuiData->IsWindowVisible)
+    {
+        /* Set the reference count */
+        if (Show) ++GuiData->MouseCursorRefCount;
+        else      --GuiData->MouseCursorRefCount;
+
+        /* Effectively show (or hide) the cursor (use special values for (w|l)Param) */
+        PostMessageW(GuiData->hWindow, WM_SETCURSOR, -1, -1);
+    }
+
+    return GuiData->MouseCursorRefCount;
+}
+
+static BOOL NTAPI
+GuiSetMouseCursor(IN OUT PFRONTEND This,
+                  HCURSOR CursorHandle)
+{
+    PGUI_CONSOLE_DATA GuiData = This->Context;
+
+    /* Do nothing if the window is hidden */
+    if (!GuiData->IsWindowVisible) return TRUE;
+
+    /*
+     * Set the cursor's handle. If the given handle is NULL,
+     * then restore the default cursor.
+     */
+    GuiData->hCursor = (CursorHandle ? CursorHandle : ghDefaultCursor);
+
+    /* Effectively modify the shape of the cursor (use special values for (w|l)Param) */
+    PostMessageW(GuiData->hWindow, WM_SETCURSOR, -1, -1);
+
+    return TRUE;
+}
+
+static HMENU NTAPI
+GuiMenuControl(IN OUT PFRONTEND This,
+               UINT CmdIdLow,
+               UINT CmdIdHigh)
+{
+    PGUI_CONSOLE_DATA GuiData = This->Context;
+
+    GuiData->CmdIdLow  = CmdIdLow ;
+    GuiData->CmdIdHigh = CmdIdHigh;
+
+    return GetSystemMenu(GuiData->hWindow, FALSE);
+}
+
+static BOOL NTAPI
+GuiSetMenuClose(IN OUT PFRONTEND This,
+                BOOL Enable)
+{
+    /*
+     * NOTE: See http://www.mail-archive.com/harbour@harbour-project.org/msg27509.html
+     * or http://harbour-devel.1590103.n2.nabble.com/Question-about-hb-gt-win-CtrlHandler-usage-td4670862i20.html
+     * for more information.
+     */
+
+    PGUI_CONSOLE_DATA GuiData = This->Context;
+    HMENU hSysMenu = GetSystemMenu(GuiData->hWindow, FALSE);
+
+    if (hSysMenu == NULL) return FALSE;
+
+    GuiData->IsCloseButtonEnabled = Enable;
+    EnableMenuItem(hSysMenu, SC_CLOSE, MF_BYCOMMAND | (Enable ? MF_ENABLED : MF_GRAYED));
+
+    return TRUE;
+}
+
+static FRONTEND_VTBL GuiVtbl =
+{
+    GuiInitFrontEnd,
+    GuiDeinitFrontEnd,
+    GuiDrawRegion,
+    GuiWriteStream,
+    GuiRingBell,
+    GuiSetCursorInfo,
+    GuiSetScreenInfo,
+    GuiResizeTerminal,
+    GuiSetActiveScreenBuffer,
+    GuiReleaseScreenBuffer,
+    GuiRefreshInternalInfo,
+    GuiChangeTitle,
+    GuiChangeIcon,
+    GuiGetConsoleWindowHandle,
+    GuiGetLargestConsoleWindowSize,
+    GuiGetSelectionInfo,
+    GuiSetPalette,
+    GuiGetDisplayMode,
+    GuiSetDisplayMode,
+    GuiShowMouseCursor,
+    GuiSetMouseCursor,
+    GuiMenuControl,
+    GuiSetMenuClose,
+};
+
+
+NTSTATUS NTAPI
+GuiLoadFrontEnd(IN OUT PFRONTEND FrontEnd,
+                IN OUT PCONSOLE_INFO ConsoleInfo,
+                IN OUT PVOID ExtraConsoleInfo,
+                IN ULONG ProcessId)
+{
+    PCONSOLE_INIT_INFO ConsoleInitInfo = ExtraConsoleInfo;
+    PGUI_INIT_INFO GuiInitInfo;
+
+    if (FrontEnd == NULL || ConsoleInfo == NULL || ConsoleInitInfo == NULL)
+        return STATUS_INVALID_PARAMETER;
+
+    /* Initialize GUI terminal emulator common functionalities */
+    if (!GuiInit()) return STATUS_UNSUCCESSFUL;
+
+    /*
+     * Initialize a private initialization info structure for later use.
+     * It must be freed by a call to GuiUnloadFrontEnd or GuiInitFrontEnd.
+     */
+    GuiInitInfo = ConsoleAllocHeap(HEAP_ZERO_MEMORY, sizeof(GUI_INIT_INFO));
+    if (GuiInitInfo == NULL) return STATUS_NO_MEMORY;
+
+    // HACK: We suppose that the pointers will be valid in GuiInitFrontEnd...
+    // If not, then copy exactly what we need in GuiInitInfo.
+    GuiInitInfo->ConsoleInfo      = ConsoleInfo;
+    GuiInitInfo->ConsoleStartInfo = ConsoleInitInfo->ConsoleStartInfo;
+    GuiInitInfo->ProcessId        = ProcessId;
+    GuiInitInfo->IsWindowVisible  = ConsoleInitInfo->IsWindowVisible;
+
+    /* Finally, initialize the frontend structure */
+    FrontEnd->Vtbl     = &GuiVtbl;
+    FrontEnd->Context  = NULL;
+    FrontEnd->Context2 = GuiInitInfo;
+
+    return STATUS_SUCCESS;
+}
+
+NTSTATUS NTAPI
+GuiUnloadFrontEnd(IN OUT PFRONTEND FrontEnd)
+{
+    if (FrontEnd == NULL) return STATUS_INVALID_PARAMETER;
+
+    if (FrontEnd->Context ) GuiDeinitFrontEnd(FrontEnd);
+    if (FrontEnd->Context2) ConsoleFreeHeap(FrontEnd->Context2);
+
+    return STATUS_SUCCESS;
+}
+
+/* EOF */