0030579: Draw Harness, Draw_Interpretor - catch exceptions other than Standard_Failure
[occt.git] / src / Draw / Draw_Window.cxx
index 8d102b8..55a659b 100644 (file)
@@ -15,7 +15,7 @@
 // commercial license or contractual agreement.
 
 // include windows.h first to have all definitions available
-#ifdef WNT
+#ifdef _WIN32
 #include <windows.h>
 #endif
 
 
 #include <tcl.h>
 #include <Draw_Interpretor.hxx>
+#include <Draw_Window.hxx>
 #include <Draw_Appli.hxx>
 #include <TCollection_AsciiString.hxx>
+#include <TCollection_ExtendedString.hxx>
 #include <Image_AlienPixMap.hxx>
+#include <NCollection_List.hxx>
 
-extern Draw_Interpretor theCommands;
+extern Standard_Boolean Draw_Batch;
 extern Standard_Boolean Draw_VirtualWindows;
-static Tcl_Interp *interp;        /* Interpreter for this application. */
+static NCollection_List<Draw_Window::FCallbackBeforeTerminate> MyCallbacks;
+
+void Draw_Window::AddCallbackBeforeTerminate(FCallbackBeforeTerminate theCB)
+{
+  MyCallbacks.Append(theCB);
+}
+
+void Draw_Window::RemoveCallbackBeforeTerminate(FCallbackBeforeTerminate theCB)
+{
+  NCollection_List<Draw_Window::FCallbackBeforeTerminate>::Iterator Iter(MyCallbacks);
+  for(; Iter.More(); Iter.Next())
+  {
+    if (Iter.Value() == theCB)
+    {
+      MyCallbacks.Remove(Iter);
+      break;
+    }
+  }
+}
 
 /*
  *----------------------------------------------------------------------
@@ -51,26 +72,16 @@ static Tcl_Interp *interp;        /* Interpreter for this application. */
 
 static void Prompt(Tcl_Interp *Interp, int partial)
 {
-
-  // MKV 29.03.05
-#if ((TCL_MAJOR_VERSION > 8) || ((TCL_MAJOR_VERSION == 8) && (TCL_MINOR_VERSION >= 4))) && !defined(USE_NON_CONST)
-    const char *promptCmd;
-#else
-    char *promptCmd;
-#endif
-    int code;
-    Tcl_Channel  outChannel, errChannel;
-    outChannel = Tcl_GetStdChannel(TCL_STDOUT);
-    promptCmd = Tcl_GetVar(Interp,(char*)
-        (partial ? "tcl_prompt2" : "tcl_prompt1"), TCL_GLOBAL_ONLY);
-
+    Tcl_Channel errChannel;
+    Tcl_Channel outChannel = Tcl_GetStdChannel(TCL_STDOUT);
+    const char* promptCmd = Tcl_GetVar (Interp, partial ? "tcl_prompt2" : "tcl_prompt1", TCL_GLOBAL_ONLY);
     if (promptCmd == NULL) {
 defaultPrompt:
       if (!partial && outChannel) {
         Tcl_Write(outChannel, "% ", 2);
       }
     } else {
-      code = Tcl_Eval(Interp, promptCmd);
+      int code = Tcl_Eval(Interp, promptCmd);
       outChannel = Tcl_GetStdChannel(TCL_STDOUT);
       errChannel = Tcl_GetStdChannel(TCL_STDERR);
       if (code != TCL_OK) {
@@ -94,43 +105,9 @@ defaultPrompt:
 
 #if !defined(_WIN32) && !defined(__WIN32__)
 
-#ifdef HAVE_CONFIG_H
-# include <config.h>
-#endif
-
 #include <OSD_Timer.hxx>
-
-#ifdef HAVE_SYS_TIME_H
-# include <sys/time.h>
-#endif
-
-#ifdef HAVE_SYS_TYPES_H
-# include <sys/types.h>
-#endif
-
-#ifdef HAVE_SYS_SELECT_H
-#include <sys/select.h>
-#endif
-
-#ifdef HAVE_SYS_FILIO_H
-#include <sys/filio.h>
-#else
-#include <sys/ioctl.h>
-#endif
-
-#include <fcntl.h>
-
-#ifdef HAVE_UNISTD_H
-# include <unistd.h>
-#endif
-
 #include <Draw_Window.hxx>
-
-#ifdef HAVE_STRINGS_H
-# include <strings.h>
-#endif
-
-#include <stdio.h>
+#include <unistd.h>
 
 #if defined(__APPLE__) && !defined(MACOSX_USE_GLX)
   // use forward declaration for small subset of used Tk functions
@@ -384,6 +361,9 @@ void Draw_Window::Init(Standard_Integer X, Standard_Integer Y,
     // advise to the window manager to place it where I need
     XSetWMNormalHints(Draw_WindowDisplay,win,&myHints);
 
+    Atom aDeleteWindowAtom = Draw_DisplayConnection->GetAtom (Aspect_XA_DELETE_WINDOW);
+    XSetWMProtocols (Draw_WindowDisplay, win, &aDeleteWindowAtom, 1);
+
     if (Draw_VirtualWindows)
     {
       myUseBuffer = Standard_True;
@@ -539,20 +519,20 @@ Standard_Integer Draw_Window::WidthWin() const
 //function : SetTitle
 //purpose  :
 //=======================================================================
-void Draw_Window::SetTitle(const char* title)
+void Draw_Window::SetTitle(const TCollection_AsciiString& theTitle)
 {
-  XStoreName(Draw_WindowDisplay, win, title);
+  XStoreName (Draw_WindowDisplay, win, theTitle.ToCString());
 }
 
 //=======================================================================
 //function : GetTitle
 //purpose  :
 //=======================================================================
-char* Draw_Window::GetTitle()
+TCollection_AsciiString Draw_Window::GetTitle() const
 {
-  char* title;
-  XFetchName(Draw_WindowDisplay, win, &title);
-  return title;
+  char* aTitle = NULL;
+  XFetchName (Draw_WindowDisplay, win, &aTitle);
+  return TCollection_AsciiString (aTitle);
 }
 
 //=======================================================================
@@ -580,6 +560,25 @@ Standard_Boolean Draw_Window::DefineColor(const Standard_Integer i, const char*
   return Standard_True;
 }
 
+//=======================================================================
+//function : IsMapped
+//purpose  :
+//=======================================================================
+bool Draw_Window::IsMapped() const
+{
+  if (Draw_VirtualWindows
+   || win == 0)
+  {
+    return false;
+  }
+
+  XFlush (Draw_WindowDisplay);
+  XWindowAttributes aWinAttr;
+  XGetWindowAttributes (Draw_WindowDisplay, win, &aWinAttr);
+  return aWinAttr.map_state == IsUnviewable
+      || aWinAttr.map_state == IsViewable;
+}
+
 //=======================================================================
 //function : DisplayWindow
 //purpose  :
@@ -748,7 +747,7 @@ Standard_Boolean Draw_Window::Save (const char* theFileName) const
   Image_AlienPixMap anImage;
   bool isBigEndian = Image_PixMap::IsBigEndianHost();
   const Standard_Size aSizeRowBytes = Standard_Size(winAttr.width) * 4;
-  if (!anImage.InitTrash (isBigEndian ? Image_PixMap::ImgRGB32 : Image_PixMap::ImgBGR32,
+  if (!anImage.InitTrash (isBigEndian ? Image_Format_RGB32 : Image_Format_BGR32,
                           Standard_Size(winAttr.width), Standard_Size(winAttr.height), aSizeRowBytes))
   {
     return Standard_False;
@@ -806,9 +805,17 @@ void ProcessEvent(Draw_Window& win, XEvent& xev)
   XComposeStatus stat;
   char chainekey[10];
 
-
-  switch (xev.type) {
-
+  switch (xev.type)
+  {
+  case ClientMessage:
+  {
+    if (xev.xclient.data.l[0] == (int )Draw_DisplayConnection->GetAtom (Aspect_XA_DELETE_WINDOW))
+    {
+      // just hide the window
+      win.Hide();
+    }
+    return;
+  }
   case Expose :
     win.WExpose();
     break;
@@ -1033,8 +1040,10 @@ void Run_Appli(Standard_Boolean (*interprete) (const char*))
 
 #endif
 
-  if (tty) Prompt(theCommands.Interp(), 0);
-  Prompt(theCommands.Interp(), 0);
+  Draw_Interpretor& aCommands = Draw::GetInterpretor();
+
+  if (tty) Prompt(aCommands.Interp(), 0);
+  Prompt(aCommands.Interp(), 0);
 
   outChannel = Tcl_GetStdChannel(TCL_STDOUT);
   if (outChannel) {
@@ -1052,7 +1061,7 @@ void Run_Appli(Standard_Boolean (*interprete) (const char*))
   if (Draw_VirtualWindows) {
     // main window will never shown
     // but main loop will parse all Xlib messages
-    Tcl_Eval(theCommands.Interp(), "wm withdraw .");
+    Tcl_Eval(aCommands.Interp(), "wm withdraw .");
   }
   Tk_MainLoop();
 
@@ -1075,6 +1084,11 @@ void Run_Appli(Standard_Boolean (*interprete) (const char*))
     }
 
 #endif
+  NCollection_List<Draw_Window::FCallbackBeforeTerminate>::Iterator Iter(MyCallbacks);
+  for(; Iter.More(); Iter.Next())
+  {
+      (*Iter.Value())();
+  }
 }
 
 //======================================================
@@ -1083,10 +1097,11 @@ void Run_Appli(Standard_Boolean (*interprete) (const char*))
 //======================================================
 Standard_Boolean Init_Appli()
 {
-  theCommands.Init();
-  interp = theCommands.Interp();
+  Draw_Interpretor& aCommands = Draw::GetInterpretor();
+  aCommands.Init();
+  Tcl_Interp *interp = aCommands.Interp();
+  Tcl_Init (interp);
 
-  Tcl_Init(interp) ;
   try {
     OCC_CATCH_SIGNALS
     Tk_Init(interp) ;
@@ -1276,7 +1291,7 @@ static void StdinProc(ClientData clientData, int )
    */
 
 prompt:
-  if (tty) Prompt(interp, gotPartial);
+  if (tty) Prompt(Draw::GetInterpretor().Interp(), gotPartial);
 
  } catch (Standard_Failure) {}
 
@@ -1340,7 +1355,7 @@ int modeTab[16] = {R2_BLACK, R2_MASKPEN, R2_MASKPENNOT, R2_COPYPEN,
 HWND DrawWindow::CreateDrawWindow(HWND hWndClient, int nitem)
 {
   if (Draw_IsConsoleSubsystem) {
-    HWND aWin = CreateWindow (DRAWCLASS, DRAWTITLE,
+    HWND aWin = CreateWindowW (DRAWCLASS, DRAWTITLE,
                               WS_OVERLAPPEDWINDOW,
                               1,1,1,1,
                               NULL, NULL,::GetModuleHandle(NULL), NULL);
@@ -1352,10 +1367,9 @@ HWND DrawWindow::CreateDrawWindow(HWND hWndClient, int nitem)
     return aWin;
   }
   else {
-    HANDLE hInstance;
-    hInstance = (HANDLE)GetWindowLongPtr(hWndClient,GWLP_HINSTANCE);
+    HANDLE hInstance = (HANDLE )GetWindowLongPtrW (hWndClient, GWLP_HINSTANCE);
 
-    return CreateMDIWindow(DRAWCLASS, DRAWTITLE,
+    return CreateMDIWindowW(DRAWCLASS, DRAWTITLE,
                            WS_CAPTION | WS_CHILD | WS_THICKFRAME,
                            1,1,0,0,
                            hWndClient, (HINSTANCE)hInstance, nitem);
@@ -1368,43 +1382,51 @@ HWND DrawWindow::CreateDrawWindow(HWND hWndClient, int nitem)
 \*--------------------------------------------------------*/
 LRESULT APIENTRY DrawWindow::DrawProc(HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam )
 {
-  DrawWindow* localObjet = (DrawWindow*)GetWindowLongPtr(hWnd, CLIENTWND);
+  DrawWindow* localObjet = (DrawWindow* )GetWindowLongPtrW (hWnd, CLIENTWND);
   if (!localObjet)
+  {
+    return Draw_IsConsoleSubsystem
+         ? DefWindowProcW   (hWnd, wMsg, wParam, lParam)
+         : DefMDIChildProcW (hWnd, wMsg, wParam, lParam);
+  }
+
+  switch (wMsg)
+  {
+    case WM_CLOSE:
     {
-      if (Draw_IsConsoleSubsystem)
-        return (DefWindowProc(hWnd, wMsg, wParam, lParam));
+      localObjet->Hide();
+      return 0; // do nothing - window destruction should be performed by application
+    }
+    case WM_PAINT:
+    {
+      PAINTSTRUCT ps;
+      BeginPaint (hWnd, &ps);
+      if (localObjet->GetUseBuffer())
+      {
+        localObjet->Redraw();
+      }
       else
-        return(DefMDIChildProc(hWnd, wMsg, wParam, lParam));
+      {
+        localObjet->WExpose();
+      }
+      EndPaint (hWnd, &ps);
+      return 0;
     }
-
-  PAINTSTRUCT ps;
-
-  switch(wMsg)
-  {
-  case WM_PAINT :
-    BeginPaint(hWnd, &ps);
-    if (localObjet->GetUseBuffer())
-      localObjet->Redraw();
-    else
-      localObjet->WExpose();
-    EndPaint(hWnd, &ps);
-    return 0l;
-    break;
-
-  case WM_SIZE:
-    if (localObjet->GetUseBuffer()) {
-      localObjet->InitBuffer();
-      localObjet->WExpose();
-      localObjet->Redraw();
-      return 0l;
+    case WM_SIZE:
+    {
+      if (localObjet->GetUseBuffer())
+      {
+        localObjet->InitBuffer();
+        localObjet->WExpose();
+        localObjet->Redraw();
+        return 0;
+      }
+      break;
     }
-
-  default:
-    if (Draw_IsConsoleSubsystem)
-      return (DefWindowProc(hWnd, wMsg, wParam, lParam));
-    else
-      return(DefMDIChildProc(hWnd, wMsg, wParam, lParam));
   }
+  return Draw_IsConsoleSubsystem
+       ? DefWindowProcW   (hWnd, wMsg, wParam, lParam)
+       : DefMDIChildProcW (hWnd, wMsg, wParam, lParam);
 }
 
 
@@ -1438,7 +1460,7 @@ DrawWindow::DrawWindow() :
 }
 
 //________________________
-DrawWindow::DrawWindow(char* title,
+DrawWindow::DrawWindow(const char* title,
                        Standard_Integer X, Standard_Integer Y,
                        Standard_Integer dX,Standard_Integer dY) :
        win(0),        next(firstWindow), previous(NULL), myMemHbm(NULL), myUseBuffer(Standard_False)
@@ -1448,7 +1470,7 @@ DrawWindow::DrawWindow(char* title,
   Init(X, Y, dX, dY);
   SetTitle(title);
 }
-DrawWindow::DrawWindow(char* title,
+DrawWindow::DrawWindow(const char* title,
                        Standard_Integer X, Standard_Integer Y,
                        Standard_Integer dX,Standard_Integer dY,
                        HWND theWin) :
@@ -1496,8 +1518,8 @@ void DrawWindow::Init(Standard_Integer theXLeft, Standard_Integer theYTop,
 
   // include decorations in the window dimensions
   // to reproduce same behaviour of Xlib window.
-  DWORD aWinStyle   = GetWindowLong (win, GWL_STYLE);
-  DWORD aWinStyleEx = GetWindowLong (win, GWL_EXSTYLE);
+  DWORD aWinStyle   = GetWindowLongW (win, GWL_STYLE);
+  DWORD aWinStyleEx = GetWindowLongW (win, GWL_EXSTYLE);
   HMENU aMenu       = GetMenu (win);
 
   RECT aRect;
@@ -1510,7 +1532,7 @@ void DrawWindow::Init(Standard_Integer theXLeft, Standard_Integer theYTop,
   SetPosition  (aRect.left, aRect.top);
   SetDimension (aRect.right - aRect.left, aRect.bottom - aRect.top);
   // Save the pointer at the instance associated to the window
-  SetWindowLongPtr(win, CLIENTWND, (LONG_PTR)this);
+  SetWindowLongPtr(win, CLIENTWND, (LONG_PTR)this);
   HDC hDC = GetDC(win);
   SetBkColor(hDC, RGB(0, 0, 0));
   myCurrPen  = 3;
@@ -1547,7 +1569,7 @@ void DrawWindow::InitBuffer()
     GetClientRect(win, &rc);
     if (myMemHbm) {
       BITMAP aBmp;
-      GetObject(myMemHbm, sizeof(BITMAP), &aBmp);
+      GetObject(myMemHbm, sizeof(BITMAP), &aBmp);
       if (rc.right-rc.left == aBmp.bmWidth && rc.bottom-rc.top == aBmp.bmHeight) return;
       DeleteObject(myMemHbm);
     }
@@ -1602,10 +1624,12 @@ void DrawWindow::ReleaseMemDC(HDC theMemDC)
 \*--------------------------------------------------------*/
 void DrawWindow::SetPosition(Standard_Integer posX, Standard_Integer posY)
 {
-  SetWindowPos(win, 0,
-               posX, posY,
-               0, 0,
-               SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER);
+  UINT aFlags = SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER;
+  if (Draw_VirtualWindows)
+  {
+    aFlags |= SWP_NOSENDCHANGING;
+  }
+  SetWindowPos (win, 0, posX, posY, 0, 0, aFlags);
 }
 
 
@@ -1614,10 +1638,12 @@ void DrawWindow::SetPosition(Standard_Integer posX, Standard_Integer posY)
 \*--------------------------------------------------------*/
 void DrawWindow::SetDimension(Standard_Integer dimX, Standard_Integer dimY)
 {
-  SetWindowPos(win, 0,
-               0, 0,
-               dimX, dimY,
-               SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER);
+  UINT aFlags = SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER;
+  if (Draw_VirtualWindows)
+  {
+    aFlags |= SWP_NOSENDCHANGING;
+  }
+  SetWindowPos (win, 0, 0, 0, dimX, dimY, aFlags);
 }
 
 
@@ -1665,23 +1691,39 @@ Standard_Integer DrawWindow::WidthWin() const
 /*--------------------------------------------------------*\
 |  SetTitle
 \*--------------------------------------------------------*/
-void DrawWindow::SetTitle(char* title)
+void DrawWindow::SetTitle (const TCollection_AsciiString& theTitle)
 {
-  SetWindowText(win, title);
+  const TCollection_ExtendedString aTitleW (theTitle);
+  SetWindowTextW (win, aTitleW.ToWideString());
 }
 
 
 /*--------------------------------------------------------*\
 |  GetTitle
-|    Attention do not forget to unallocate the memory
 \*--------------------------------------------------------*/
-char* DrawWindow::GetTitle()
+TCollection_AsciiString DrawWindow::GetTitle() const
 {
-  char* title=new char[31];
-  GetWindowText(win, title, 30);
-  return title;
+  wchar_t aTitleW[32];
+  GetWindowTextW (win, aTitleW, 30);
+  return TCollection_AsciiString (aTitleW);
 }
 
+//=======================================================================
+//function : IsMapped
+//purpose  :
+//=======================================================================
+bool Draw_Window::IsMapped() const
+{
+  if (Draw_VirtualWindows
+   || win == NULL)
+  {
+    return false;
+  }
+
+  LONG aWinStyle = GetWindowLongW (win, GWL_STYLE);
+  return (aWinStyle & WS_VISIBLE)  != 0
+      && (aWinStyle & WS_MINIMIZE) == 0;
+}
 
 /*--------------------------------------------------------*\
 |  DisplayWindow
@@ -1741,14 +1783,14 @@ static Standard_Boolean SaveBitmap (HBITMAP     theHBitmap,
 {
   // Get informations about the bitmap
   BITMAP aBitmap;
-  if (GetObject (theHBitmap, sizeof(BITMAP), (LPSTR )&aBitmap) == 0)
+  if (GetObjectW (theHBitmap, sizeof(BITMAP), &aBitmap) == 0)
   {
     return Standard_False;
   }
 
   Image_AlienPixMap anImage;
-  const Standard_Size aSizeRowBytes = Standard_Size(aBitmap.bmWidth) * 4;
-  if (!anImage.InitTrash (Image_PixMap::ImgBGR32, Standard_Size(aBitmap.bmWidth), Standard_Size(aBitmap.bmHeight), aSizeRowBytes))
+  const Standard_Size aSizeRowBytes = ((Standard_Size(aBitmap.bmWidth) * 24 + 31) / 32) * 4; // 4 bytes alignment for GetDIBits()
+  if (!anImage.InitTrash (Image_Format_BGR, Standard_Size(aBitmap.bmWidth), Standard_Size(aBitmap.bmHeight), aSizeRowBytes))
   {
     return Standard_False;
   }
@@ -1761,7 +1803,7 @@ static Standard_Boolean SaveBitmap (HBITMAP     theHBitmap,
   aBitmapInfo.biWidth       = aBitmap.bmWidth;
   aBitmapInfo.biHeight      = aBitmap.bmHeight; // positive means bottom-up!
   aBitmapInfo.biPlanes      = 1;
-  aBitmapInfo.biBitCount    = 32; // use 32bit for automatic word-alignment per row
+  aBitmapInfo.biBitCount    = 24;
   aBitmapInfo.biCompression = BI_RGB;
 
   // Copy the pixels
@@ -1818,7 +1860,8 @@ void DrawWindow::DrawString(int x,int y, char* text)
   HDC hDC = GetDC(win);
   HDC aWorkDC = myUseBuffer ? GetMemDC(hDC) : hDC;
 
-  TextOut(aWorkDC, x, y, text, (int )strlen(text));
+  TCollection_ExtendedString textW (text);
+  TextOutW(aWorkDC, x, y, (const wchar_t*)textW.ToExtString(), (int )strlen(text));
 
   if (myUseBuffer) ReleaseMemDC(aWorkDC);
   ReleaseDC(win,hDC);
@@ -1968,9 +2011,7 @@ static Tk_Window mainWindow;
 //* threads sinchronization *//
 DWORD  dwMainThreadId;
 console_semaphore_value volatile console_semaphore = WAIT_CONSOLE_COMMAND;
-//char console_command[1000];
-#define COMMAND_SIZE 1000     /* Console Command size */
-char console_command[COMMAND_SIZE];
+wchar_t console_command[DRAW_COMMAND_SIZE + 1];
 bool volatile isTkLoopStarted = false;
 
 /*--------------------------------------------------------*\
@@ -1982,9 +2023,6 @@ Standard_Boolean Init_Appli(HINSTANCE hInst,
   DWORD IDThread;
   HANDLE hThread;
   console_semaphore = STOP_CONSOLE;
-  theCommands.Init();
-  interp = theCommands.Interp();
-  Tcl_Init(interp) ;
 
   dwMainThreadId = GetCurrentThreadId();
 
@@ -1996,13 +2034,18 @@ Standard_Boolean Init_Appli(HINSTANCE hInst,
                            0,                       // use default creation flags
                            &IDThread);
   if (!hThread) {
-    cout << "Tcl/Tk main loop thread not created. Switching to batch mode..." << endl;
+    cout << "Failed to create Tcl/Tk main loop thread. Switching to batch mode..." << endl;
+    Draw_Batch = Standard_True;
+    Draw_Interpretor& aCommands = Draw::GetInterpretor();
+    aCommands.Init();
+    Tcl_Interp *interp = aCommands.Interp();
+    Tcl_Init(interp);
 #ifdef _TK
     try {
       OCC_CATCH_SIGNALS
-      Tk_Init(interp) ;
-    } catch  (Standard_Failure) {
-      cout <<" Pb au lancement de TK_Init "<<endl;
+      Tk_Init(interp);
+    } catch  (Standard_Failure& anExcept) {
+      cout << "Failed to initialize Tk: " << anExcept.GetMessageString() << endl;
     }
 
     Tcl_StaticPackage(interp, "Tk", Tk_Init, (Tcl_PackageInitProc *) NULL);
@@ -2039,16 +2082,62 @@ Standard_Boolean Draw_Interprete (const char*);
 /*--------------------------------------------------------*\
 |  readStdinThreadFunc
 \*--------------------------------------------------------*/
-static DWORD WINAPI readStdinThreadFunc(VOID)
+static DWORD WINAPI readStdinThreadFunc()
 {
-  if (!Draw_IsConsoleSubsystem) return 1;
-  for(;;) {
+  if (!Draw_IsConsoleSubsystem)
+  {
+    return 1;
+  }
+
+  // Console locale could be set to the system codepage .OCP (UTF-8 is not properly supported on Windows).
+  // However, to use it, we have to care using std::wcerr/fwprintf/WriteConsoleW for non-ascii strings everywhere (including Tcl itself),
+  // or otherwise we can have incomplete output issues
+  // (e.g. UNICODE string will be NOT just corrupted as in case when we don't set setlocale()
+  // but will break further normal output to console due to special characters being accidentally handled by console in the wrong way).
+  //setlocale (LC_ALL, ".OCP");
+
+  // _O_U16TEXT can be used with fgetws() to get similar result as ReadConsoleW() without affecting setlocale(),
+  // however it would break pipe input
+  //_setmode (_fileno(stdin), _O_U16TEXT);
+
+  bool isConsoleInput = true;
+  for (;;)
+  {
     while (console_semaphore != WAIT_CONSOLE_COMMAND)
-      Sleep(100);
-      if (fgets(console_command,COMMAND_SIZE,stdin))
+    {
+      Sleep (100);
+    }
+
+    const HANDLE anStdIn = ::GetStdHandle (STD_INPUT_HANDLE);
+    if (anStdIn != NULL
+     && anStdIn != INVALID_HANDLE_VALUE
+     && isConsoleInput)
+    {
+      DWORD aNbRead = 0;
+      if (ReadConsoleW (anStdIn, console_command, DRAW_COMMAND_SIZE, &aNbRead, NULL))
       {
+        console_command[aNbRead] = L'\0';
         console_semaphore = HAS_CONSOLE_COMMAND;
+        continue;
       }
+      else
+      {
+        const DWORD anErr = GetLastError();
+        if (anErr != ERROR_SUCCESS)
+        {
+          // fallback using fgetws() which would work with pipes
+          // but supports Unicode only through multi-byte encoding (which is not UTF-8)
+          isConsoleInput = false;
+          continue;
+        }
+      }
+    }
+
+    // fgetws() works only for characters within active locale (see setlocale())
+    if (fgetws (console_command, DRAW_COMMAND_SIZE, stdin))
+    {
+      console_semaphore = HAS_CONSOLE_COMMAND;
+    }
   }
 }
 
@@ -2057,20 +2146,148 @@ static DWORD WINAPI readStdinThreadFunc(VOID)
 \*--------------------------------------------------------*/
 void exitProc(ClientData /*dc*/)
 {
+  NCollection_List<Draw_Window::FCallbackBeforeTerminate>::Iterator Iter(MyCallbacks);
+  for(; Iter.More(); Iter.Next())
+  {
+      (*Iter.Value())();
+  }
   HANDLE proc = GetCurrentProcess();
   TerminateProcess(proc, 0);
 }
 
+// This is fixed version of TclpGetDefaultStdChannel() defined in tclWinChan.c
+// See https://core.tcl.tk/tcl/tktview/91c9bc1c457fda269ae18595944fc3c2b54d961d
+static Tcl_Channel
+TclpGetDefaultStdChannel(
+    int type)                  /* One of TCL_STDIN, TCL_STDOUT, or
+                                * TCL_STDERR. */
+{
+    Tcl_Channel channel;
+    HANDLE handle;
+    int mode = -1;
+    const char *bufMode = NULL;
+    DWORD handleId = (DWORD) -1;
+                               /* Standard handle to retrieve. */
+
+    switch (type) {
+    case TCL_STDIN:
+       handleId = STD_INPUT_HANDLE;
+       mode = TCL_READABLE;
+       bufMode = "line";
+       break;
+    case TCL_STDOUT:
+       handleId = STD_OUTPUT_HANDLE;
+       mode = TCL_WRITABLE;
+       bufMode = "line";
+       break;
+    case TCL_STDERR:
+       handleId = STD_ERROR_HANDLE;
+       mode = TCL_WRITABLE;
+       bufMode = "none";
+       break;
+    default:
+       Tcl_Panic("TclGetDefaultStdChannel: Unexpected channel type");
+       break;
+    }
+
+    handle = GetStdHandle(handleId);
+
+    /*
+     * Note that we need to check for 0 because Windows may return 0 if this
+     * is not a console mode application, even though this is not a valid
+     * handle.
+     */
+
+    if ((handle == INVALID_HANDLE_VALUE) || (handle == 0)) {
+       return (Tcl_Channel) NULL;
+    }
+
+    /*
+     * Make duplicate of the standard handle as it may be altered
+     * (closed, reopened with another type of the object etc.) by
+     * the system or a user code at any time, e.g. by call to _dup2()
+     */
+    if (! DuplicateHandle (GetCurrentProcess(), handle, 
+                           GetCurrentProcess(), &handle,
+                           0, FALSE, DUPLICATE_SAME_ACCESS)) {
+       return (Tcl_Channel) NULL;
+    }
+
+    channel = Tcl_MakeFileChannel(handle, mode);
+
+    if (channel == NULL) {
+       return (Tcl_Channel) NULL;
+    }
+
+    /*
+     * Set up the normal channel options for stdio handles.
+     */
+
+    if (Tcl_SetChannelOption(NULL,channel,"-translation","auto")!=TCL_OK ||
+           Tcl_SetChannelOption(NULL,channel,"-eofchar","\032 {}")!=TCL_OK ||
+           Tcl_SetChannelOption(NULL,channel,"-buffering",bufMode)!=TCL_OK) {
+       Tcl_Close(NULL, channel);
+       return (Tcl_Channel) NULL;
+    }
+    return channel;
+}
+
+// helper functuion
+static void ResetStdChannel (int type)
+{
+  Tcl_Channel aChannel = TclpGetDefaultStdChannel (type);
+  Tcl_SetStdChannel (aChannel, type);
+  if (aChannel)
+  {
+    Tcl_RegisterChannel (NULL, aChannel);
+  }
+}
+
 /*--------------------------------------------------------*\
 |  tkLoop: implements Tk_Main()-like behaviour in a separate thread
 \*--------------------------------------------------------*/
 static DWORD WINAPI tkLoop(VOID)
 {
   Tcl_CreateExitHandler(exitProc, 0);
+  
+  Draw_Interpretor& aCommands = Draw::GetInterpretor();
+  aCommands.Init();
+  Tcl_Interp *interp = aCommands.Interp();
+  Tcl_Init(interp);
+
+  // Work-around against issue with Tcl standard channels on Windows.
+  // These channels by default use OS handles owned by the system which
+  // may get invalidated e.g. by dup2() (see dlog command).
+  // If this happens, output to stdout from Tcl (e.g. puts) gets broken
+  // (sympthom is error message: "error writing "stdout": bad file number").
+  // To prevent this, we set standard channels using duplicate of system handles.
+  // The effect is that Tcl channel becomes independent on C file descriptor
+  // and even if stdout/stderr are redirected using dup2(), Tcl keeps using
+  // original device.
+  ResetStdChannel (TCL_STDOUT);
+  ResetStdChannel (TCL_STDERR);
+
 #if (TCL_MAJOR_VERSION > 8) || ((TCL_MAJOR_VERSION == 8) && (TCL_MINOR_VERSION >= 5))
-  Tcl_RegisterChannel(theCommands.Interp(),  Tcl_GetStdChannel(TCL_STDIN));
-  Tcl_RegisterChannel(theCommands.Interp(),  Tcl_GetStdChannel(TCL_STDOUT));
-  Tcl_RegisterChannel(theCommands.Interp(),  Tcl_GetStdChannel(TCL_STDERR));
+  // Plain Tcl (8.6.4+) initializes interpretor channels automatically, but 
+  // ActiveState Tcl (at least 8.6.4) does not seem to do that, so channels 
+  // need to be set into interpretor explicitly
+  {
+    Tcl_Channel aChannelIn  = Tcl_GetStdChannel (TCL_STDIN);
+    Tcl_Channel aChannelOut = Tcl_GetStdChannel (TCL_STDOUT);
+    Tcl_Channel aChannelErr = Tcl_GetStdChannel (TCL_STDERR);
+    if (aChannelIn != NULL)
+    {
+      Tcl_RegisterChannel (aCommands.Interp(), aChannelIn);
+    }
+    if (aChannelOut != NULL)
+    {
+      Tcl_RegisterChannel (aCommands.Interp(), aChannelOut);
+    }
+    if (aChannelErr != NULL)
+    {
+      Tcl_RegisterChannel (aCommands.Interp(), aChannelErr);
+    }
+  }
 #endif
 
 #ifdef _TK
@@ -2113,7 +2330,7 @@ static DWORD WINAPI tkLoop(VOID)
 #endif //#ifdef _TK
 
   // set signal handler in the new thread
-  OSD::SetSignal();
+  OSD::SetSignal(Standard_False);
 
   // inform the others that we have started
   isTkLoopStarted = true;
@@ -2131,7 +2348,8 @@ static DWORD WINAPI tkLoop(VOID)
     while(Tcl_DoOneEvent(TCL_ALL_EVENTS | TCL_DONT_WAIT));
     if (console_semaphore == HAS_CONSOLE_COMMAND)
     {
-      if (Draw_Interprete (console_command))
+      TCollection_AsciiString aCmdUtf8 (console_command);
+      if (Draw_Interprete (aCmdUtf8.ToCString()))
       {
         if (Draw_IsConsoleSubsystem) Prompt (interp, 0);
       }
@@ -2147,7 +2365,7 @@ static DWORD WINAPI tkLoop(VOID)
     }
   #ifdef _TK
     // We should not exit until the Main Tk window is closed
-    toLoop = (Tk_GetNumMainWindows() > 0) || Draw_VirtualWindows;
+    toLoop = (Draw_VirtualWindows || Tk_GetNumMainWindows() > 0);
   #endif
   }
   Tcl_Exit(0);
@@ -2179,9 +2397,9 @@ void Run_Appli(HWND hWnd)
     if (!hThread) {
       cout << "pb in creation of the thread reading stdin" << endl;
       Draw_IsConsoleSubsystem = Standard_False;
-      Init_Appli(GetModuleHandle(NULL),
-                 GetModuleHandle(NULL),
-                 1, hWnd); // reinit => create MDI client wnd
+      Init_Appli (GetModuleHandleW (NULL),
+                  GetModuleHandleW (NULL),
+                  1, hWnd); // reinit => create MDI client wnd
     }
   }
 
@@ -2190,12 +2408,12 @@ void Run_Appli(HWND hWnd)
     console_semaphore = WAIT_CONSOLE_COMMAND;
 
   //simple Win32 message loop
-  while (GetMessage(&msg, NULL, 0, 0) > 0)
+  while (GetMessage(&msg, NULL, 0, 0) > 0)
   {
-    if (!TranslateAccelerator(hWnd, hAccel, &msg))
+    if (!TranslateAccelerator(hWnd, hAccel, &msg))
     {
-      TranslateMessage(&msg);
-      DispatchMessage(&msg);
+      TranslateMessage (&msg);
+      DispatchMessage(&msg);
     }
   }
   ExitProcess(0);
@@ -2221,10 +2439,12 @@ void DrawWindow::SelectWait(HANDLE& hWnd, int& x, int& y, int& button)
 
   msg.wParam = 1;
 
-  GetMessage(&msg,NULL,0,0);
+  GetMessageW (&msg, NULL, 0, 0);
   while((msg.message != WM_RBUTTONDOWN && msg.message != WM_LBUTTONDOWN) ||
         ! ( Draw_IsConsoleSubsystem || IsChild(DrawWindow::hWndClientMDI,msg.hwnd)) )
-    GetMessage(&msg,NULL,0,0);
+  {
+    GetMessageW (&msg, NULL, 0, 0);
+  }
 
   hWnd = msg.hwnd;
   x = LOWORD(msg.lParam);
@@ -2244,11 +2464,13 @@ void DrawWindow::SelectNoWait(HANDLE& hWnd, int& x, int& y, int& button)
 
   msg.wParam = 1;
 
-  GetMessage(&msg,NULL,0,0);
+  GetMessage(&msg,NULL,0,0);
   while((msg.message != WM_RBUTTONDOWN && msg.message != WM_LBUTTONDOWN &&
         msg.message != WM_MOUSEMOVE) ||
         ! ( Draw_IsConsoleSubsystem || IsChild(DrawWindow::hWndClientMDI,msg.hwnd) ) )
-    GetMessage(&msg,NULL,0,0);
+  {
+    GetMessageW(&msg,NULL,0,0);
+  }
   hWnd = msg.hwnd;
   x = LOWORD(msg.lParam);
   y = HIWORD(msg.lParam);