1 // Created on: 1994-07-27
2 // Created by: Remi LEQUETTE
3 // Copyright (c) 1994-1999 Matra Datavision
4 // Copyright (c) 1999-2014 OPEN CASCADE SAS
5 //
6 // This file is part of Open CASCADE Technology software library.
7 //
8 // This library is free software; you can redistribute it and/or modify it under
9 // the terms of the GNU Lesser General Public License version 2.1 as published
10 // by the Free Software Foundation, with special exception defined in the file
11 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
12 // distribution for complete text of the license and disclaimer of any warranty.
13 //
14 // Alternatively, this file may be used under the terms of Open CASCADE
15 // commercial license or contractual agreement.
16 
17 // include windows.h first to have all definitions available
18 #ifdef _WIN32
19 #include <windows.h>
20 #endif
21 
22 #include <Draw_Window.hxx>
23 
24 #include <Aspect_DisplayConnection.hxx>
25 #include <Draw_Appli.hxx>
26 #include <Draw_Interpretor.hxx>
27 #include <Image_AlienPixMap.hxx>
28 #include <Message.hxx>
29 #include <NCollection_List.hxx>
30 #include <OSD.hxx>
31 #include <OSD_Timer.hxx>
32 #include <Standard_ErrorHandler.hxx>
33 #include <TCollection_AsciiString.hxx>
34 #include <TCollection_ExtendedString.hxx>
35 
36 #include <tcl.h>
37 
38 #if !defined(_WIN32)
39   #include <unistd.h>
40 #endif
41 
42 #if defined(__EMSCRIPTEN__)
43   #include <emscripten/emscripten.h>
44 
45   //! Returns Module.noExitRuntime flag.
46   EM_JS(bool, occJSModuleNoExitRuntime, (), {
47     return Module.noExitRuntime === true;
48   });
49 #endif
50 
51 #ifdef HAVE_TK
52 #if defined(__APPLE__) && !defined(HAVE_XLIB)
53   // use forward declaration for small subset of used Tk functions
54   // to workaround broken standard Tk framework installation within OS X SDKs
55   // which *HAS* X11 headers in Tk.framework but doesn't install them appropriately
56   #define _TK
57   typedef struct Tk_Window_* Tk_Window;
58   typedef const char* Tk_Uid;
59 
60   extern "C" int Tk_Init (Tcl_Interp* interp);
61   extern "C" void Tk_MainLoop();
62   extern "C" Tk_Window Tk_MainWindow (Tcl_Interp* interp) ;
63   extern "C" Tk_Uid Tk_GetUid (const char* str);
64   extern "C" const char* Tk_SetAppName (Tk_Window tkwin, const char* name) ;
65   extern "C" void Tk_GeometryRequest (Tk_Window tkwin, int reqWidth, int reqHeight);
66 
67 #else
68   #include <tk.h>
69 #endif
70 #endif
71 
72 #if defined(HAVE_XLIB)
73   #include <X11/Xutil.h>
74 #endif
75 
76 #if defined(_WIN32)
77 
78 #include "Draw_WNTRessource.pxx"
79 #include "Draw_WNTInit.pxx"
80 
81 #define PENWIDTH 1
82 #define CLIENTWND 0
83 
84 //! Creation of color stylos
85 static HPEN Draw_colorPenTab[MAXCOLOR] =
86 {
87   CreatePen(PS_SOLID, PENWIDTH, RGB(255,255,255)),
88   CreatePen(PS_SOLID, PENWIDTH, RGB(255,0,0)),
89   CreatePen(PS_SOLID, PENWIDTH, RGB(0,255,0)),
90   CreatePen(PS_SOLID, PENWIDTH, RGB(0,0,255)),
91   CreatePen(PS_SOLID, PENWIDTH, RGB(0,255,255)),
92   CreatePen(PS_SOLID, PENWIDTH, RGB(255,215,0)),
93   CreatePen(PS_SOLID, PENWIDTH, RGB(255,0,255)),
94   CreatePen(PS_SOLID, PENWIDTH, RGB(255,52,179)),
95   CreatePen(PS_SOLID, PENWIDTH, RGB(255,165,0)),
96   CreatePen(PS_SOLID, PENWIDTH, RGB(255,228,225)),
97   CreatePen(PS_SOLID, PENWIDTH, RGB(255,160,122)),
98   CreatePen(PS_SOLID, PENWIDTH, RGB(199,21,133)),
99   CreatePen(PS_SOLID, PENWIDTH, RGB(255,255,0)),
100   CreatePen(PS_SOLID, PENWIDTH, RGB(240,230,140)),
101   CreatePen(PS_SOLID, PENWIDTH, RGB(255,127,80))
102 };
103 
104 // Correspondence mode X11 and WINDOWS NT
105 static const int Draw_modeTab[16] =
106 {
107   R2_BLACK, R2_MASKPEN, R2_MASKPENNOT, R2_COPYPEN,
108   R2_MASKNOTPEN, R2_NOP, R2_XORPEN, R2_MERGEPEN,
109   R2_NOTMASKPEN, R2_NOTXORPEN, R2_NOT, R2_MERGEPENNOT,
110   R2_NOTCOPYPEN, R2_MERGENOTPEN, R2_NOTMERGEPEN, R2_WHITE
111 };
112 #endif
113 
114 extern Standard_Boolean Draw_Batch;
115 extern Standard_Boolean Draw_VirtualWindows;
116 Standard_Boolean Draw_BlackBackGround = Standard_True;
117 #if defined(_WIN32)
118 // indicates SUBSYSTEM:CONSOLE linker option, to be set to True in main()
119 Standard_EXPORT Standard_Boolean Draw_IsConsoleSubsystem = Standard_False;
120 HWND Draw_Window::hWndClientMDI = 0;
121 #endif
122 
123 //! Return termination callbacks.
TermCallbacks()124 static NCollection_List<Draw_Window::FCallbackBeforeTerminate>& TermCallbacks()
125 {
126   static NCollection_List<Draw_Window::FCallbackBeforeTerminate> MyCallbacks;
127   return MyCallbacks;
128 }
129 
130 //=======================================================================
131 //function : AddCallbackBeforeTerminate
132 //purpose  :
133 //=======================================================================
AddCallbackBeforeTerminate(FCallbackBeforeTerminate theCB)134 void Draw_Window::AddCallbackBeforeTerminate (FCallbackBeforeTerminate theCB)
135 {
136   TermCallbacks().Append (theCB);
137 }
138 
139 //=======================================================================
140 //function : RemoveCallbackBeforeTerminate
141 //purpose  :
142 //=======================================================================
RemoveCallbackBeforeTerminate(FCallbackBeforeTerminate theCB)143 void Draw_Window::RemoveCallbackBeforeTerminate (FCallbackBeforeTerminate theCB)
144 {
145   for (NCollection_List<Draw_Window::FCallbackBeforeTerminate>::Iterator anIter (TermCallbacks());
146        anIter.More(); anIter.Next())
147   {
148     if (anIter.Value() == theCB)
149     {
150       TermCallbacks().Remove (anIter);
151       break;
152     }
153   }
154 }
155 
156 //! Issue a prompt on standard output, or invoke a script to issue the prompt.
157 //! Side effects: A prompt gets output, and a Tcl script may be evaluated in interp.
Prompt(Tcl_Interp * theInterp,int thePartial)158 static void Prompt (Tcl_Interp* theInterp, int thePartial)
159 {
160   Tcl_Channel errChannel;
161   Tcl_Channel outChannel = Tcl_GetStdChannel(TCL_STDOUT);
162   const char* promptCmd = Tcl_GetVar (theInterp, thePartial ? "tcl_prompt2" : "tcl_prompt1", TCL_GLOBAL_ONLY);
163   if (promptCmd == NULL)
164   {
165 defaultPrompt:
166     if (!thePartial && outChannel)
167     {
168       Tcl_Write(outChannel, "% ", 2);
169     }
170   }
171   else
172   {
173     int code = Tcl_Eval (theInterp, promptCmd);
174     outChannel = Tcl_GetStdChannel (TCL_STDOUT);
175     errChannel = Tcl_GetStdChannel (TCL_STDERR);
176     if (code != TCL_OK)
177     {
178       if (errChannel)
179       {
180 #if ((TCL_MAJOR_VERSION > 8) || ((TCL_MAJOR_VERSION == 8) && (TCL_MINOR_VERSION >= 5)))
181         Tcl_Write (errChannel, Tcl_GetStringResult (theInterp), -1);
182 #else
183         Tcl_Write (errChannel, theInterp->result, -1);
184 #endif
185         Tcl_Write (errChannel, "\n", 1);
186       }
187       Tcl_AddErrorInfo (theInterp,
188                         "\n    (script that generates prompt)");
189       goto defaultPrompt;
190     }
191   }
192   if (outChannel)
193   {
194     Tcl_Flush (outChannel);
195   }
196 }
197 
198 #if !defined(_WIN32)
199 
200 //! Used to assemble lines of terminal input into Tcl commands.
201 static Tcl_DString Draw_TclCommand;
202 //! Used to read the next line from the terminal input.
203 static Tcl_DString Draw_TclLine;
204 
205 //! Forward declarations for procedures defined later in this file:
206 static void StdinProc (ClientData theClientData, int theMask);
207 static void Prompt (Tcl_Interp* theInterp, int thePartial);
208 
209 //! Non-zero means standard input is a terminal-like device.
210 //! Zero means it's a file.
211 static Standard_Boolean tty;
212 
213 #if defined(HAVE_XLIB)
214 static unsigned long thePixels[MAXCOLOR];
215 
216 Display* Draw_WindowDisplay = NULL;
217 Colormap Draw_WindowColorMap;
218 static Standard_Integer Draw_WindowScreen = 0;
219 static Handle(Aspect_DisplayConnection) Draw_DisplayConnection;
220 
221 //! Return list of windows.
getDrawWindowList()222 static NCollection_List<Draw_Window*>& getDrawWindowList()
223 {
224   static NCollection_List<Draw_Window*> MyWindows;
225   return MyWindows;
226 }
227 
228 //! Base_Window struct definition
229 struct Draw_Window::Base_Window
230 {
231   GC gc;
232   XSetWindowAttributes xswa;
233 };
234 #endif
235 #endif
236 
237 #if !defined(__APPLE__) || defined(HAVE_XLIB) // implementation for Apple resides in .mm file
238 //=======================================================================
239 //function : Draw_Window
240 //purpose  :
241 //=======================================================================
Draw_Window(const char * theTitle,const NCollection_Vec2<int> & theXY,const NCollection_Vec2<int> & theSize,Aspect_Drawable theParent,Aspect_Drawable theWin)242 Draw_Window::Draw_Window (const char* theTitle,
243                           const NCollection_Vec2<int>& theXY,
244                           const NCollection_Vec2<int>& theSize,
245                           Aspect_Drawable theParent,
246                           Aspect_Drawable theWin)
247 : myWindow (0),
248 #if defined(_WIN32)
249   myMemHbm (NULL),
250   myCurrPen  (0),
251   myCurrMode (0),
252 #elif defined(HAVE_XLIB)
253   myMother ((Window )theParent),
254   myImageBuffer (0),
255   myBase (new Base_Window()),
256 #endif
257   myCurrentColor (0),
258   myUseBuffer (Standard_False)
259 {
260   NCollection_Vec2<int> anXY = theXY, aSize = theSize;
261 #if defined(_WIN32)
262   myWindow = (HWND )theWin;
263   (void )theParent;
264 #elif defined(HAVE_XLIB)
265   myWindow = (Window )theWin;
266   if (theParent == 0)
267   {
268     myMother = RootWindow (Draw_WindowDisplay, Draw_WindowScreen);
269   }
270   if (theWin != 0)
271   {
272     GetPosition (anXY.x(), anXY.y());
273     aSize.x() = HeightWin();
274     aSize.y() = WidthWin();
275   }
276 
277   getDrawWindowList().Append (this);
278 #else
279   (void )theParent;
280   (void )theWin;
281 #endif
282 
283   init (anXY, aSize);
284   SetTitle (theTitle);
285 }
286 
287 //=======================================================================
288 //function : ~Draw_Window
289 //purpose  :
290 //=======================================================================
~Draw_Window()291 Draw_Window::~Draw_Window()
292 {
293 #ifdef _WIN32
294   // Delete 'off-screen drawing'-related objects
295   if (myMemHbm)
296   {
297     DeleteObject (myMemHbm);
298     myMemHbm = NULL;
299   }
300 #elif defined(HAVE_XLIB)
301   getDrawWindowList().Remove (this);
302   if (myImageBuffer != 0)
303   {
304     XFreePixmap (Draw_WindowDisplay, myImageBuffer);
305     myImageBuffer = 0;
306   }
307 #endif
308 }
309 
310 //=======================================================================
311 //function : init
312 //purpose  :
313 //=======================================================================
init(const NCollection_Vec2<int> & theXY,const NCollection_Vec2<int> & theSize)314 void Draw_Window::init (const NCollection_Vec2<int>& theXY,
315                         const NCollection_Vec2<int>& theSize)
316 {
317 #ifdef _WIN32
318   if (myWindow == NULL)
319   {
320     myWindow = createDrawWindow (hWndClientMDI, 0);
321   }
322 
323   // include decorations in the window dimensions
324   // to reproduce same behaviour of Xlib window.
325   DWORD aWinStyle   = GetWindowLongW (myWindow, GWL_STYLE);
326   DWORD aWinStyleEx = GetWindowLongW (myWindow, GWL_EXSTYLE);
327   HMENU aMenu       = GetMenu (myWindow);
328 
329   RECT aRect;
330   aRect.top    = theXY.y();
331   aRect.bottom = theXY.y() + theSize.y();
332   aRect.left   = theXY.x();
333   aRect.right  = theXY.x() + theSize.x();
334   AdjustWindowRectEx (&aRect, aWinStyle, aMenu != NULL ? TRUE : FALSE, aWinStyleEx);
335 
336   SetPosition  (aRect.left, aRect.top);
337   SetDimension (aRect.right - aRect.left, aRect.bottom - aRect.top);
338   // Save the pointer at the instance associated to the window
339   SetWindowLongPtrW (myWindow, CLIENTWND, (LONG_PTR)this);
340   HDC hDC = GetDC (myWindow);
341   SetBkColor (hDC, RGB(0, 0, 0));
342   myCurrPen  = 3;
343   myCurrMode = 3;
344   SelectObject (hDC, Draw_colorPenTab[myCurrPen]); // Default pencil
345   SelectObject (hDC, GetStockObject(BLACK_BRUSH));
346   SetTextColor (hDC, RGB(0,0,255));
347   ReleaseDC (myWindow, hDC);
348 
349   if (Draw_VirtualWindows)
350   {
351     // create a virtual window
352     SetUseBuffer (Standard_True);
353   }
354 #elif defined(HAVE_XLIB)
355   if (Draw_BlackBackGround)
356   {
357     myBase->xswa.background_pixel = BlackPixel(Draw_WindowDisplay, Draw_WindowScreen);
358     myBase->xswa.border_pixel     = WhitePixel(Draw_WindowDisplay, Draw_WindowScreen);
359   }
360   else
361   {
362     myBase->xswa.background_pixel = WhitePixel(Draw_WindowDisplay, Draw_WindowScreen);
363     myBase->xswa.border_pixel     = BlackPixel(Draw_WindowDisplay, Draw_WindowScreen);
364   }
365   myBase->xswa.colormap = Draw_WindowColorMap;
366   unsigned long aSetMask = CWBackPixel | CWBorderPixel;
367 
368   XSizeHints aWinHints;
369   aWinHints.flags = USPosition;
370   aWinHints.x = theXY.x();
371   aWinHints.y = theXY.y();
372   if (myWindow == 0)
373   {
374     myWindow = XCreateWindow (Draw_WindowDisplay,
375                               myMother,
376                               theXY.x(), theXY.y(),
377                               (unsigned int )theSize.x(), (unsigned int )theSize.y(),
378                               5,
379                               DefaultDepth(Draw_WindowDisplay, Draw_WindowScreen),
380                               InputOutput,
381                               DefaultVisual(Draw_WindowDisplay, Draw_WindowScreen),
382                               aSetMask, &myBase->xswa);
383     XSelectInput (Draw_WindowDisplay, myWindow, ButtonPressMask | ExposureMask | StructureNotifyMask);
384 
385     // advise to the window manager to place it where I need
386     XSetWMNormalHints (Draw_WindowDisplay, myWindow, &aWinHints);
387 
388     Atom aDeleteWindowAtom = Draw_DisplayConnection->GetAtom (Aspect_XA_DELETE_WINDOW);
389     XSetWMProtocols (Draw_WindowDisplay, myWindow, &aDeleteWindowAtom, 1);
390 
391     if (Draw_VirtualWindows)
392     {
393       myUseBuffer = Standard_True;
394       InitBuffer();
395     }
396   }
397 
398   myBase->gc = XCreateGC (Draw_WindowDisplay, myWindow, 0, NULL);
399 
400   XSetPlaneMask (Draw_WindowDisplay, myBase->gc, AllPlanes);
401   XSetForeground (Draw_WindowDisplay,
402                   myBase->gc, WhitePixel(Draw_WindowDisplay, Draw_WindowScreen));
403   XSetBackground (Draw_WindowDisplay,
404                   myBase->gc, BlackPixel(Draw_WindowDisplay, Draw_WindowScreen));
405   // save in case of window recovery
406 
407   myBase->xswa.backing_store = Always;
408   XChangeWindowAttributes (Draw_WindowDisplay, myWindow,
409                            CWBackingStore, &myBase->xswa);
410 
411   XSetLineAttributes (Draw_WindowDisplay, myBase->gc,
412                       0, LineSolid, CapButt, JoinMiter);
413 #else
414   (void )theXY;
415   (void )theSize;
416 #endif
417 }
418 
419 //=======================================================================
420 //function : SetUseBuffer
421 //purpose  :
422 //=======================================================================
SetUseBuffer(Standard_Boolean theToUse)423 void Draw_Window::SetUseBuffer (Standard_Boolean theToUse)
424 {
425   myUseBuffer = theToUse;
426   InitBuffer();
427 }
428 
429 #ifdef _WIN32
430 //=======================================================================
431 //function : getMemDC
432 //purpose  :
433 //=======================================================================
getMemDC(HDC theWinDC)434 HDC Draw_Window::getMemDC (HDC theWinDC)
435 {
436   if (!myUseBuffer)
437   {
438     return NULL;
439   }
440 
441   HDC aWorkDC = CreateCompatibleDC (theWinDC);
442   myOldHbm = (HBITMAP )SelectObject (aWorkDC, myMemHbm);
443   SetROP2 (aWorkDC, Draw_modeTab[myCurrMode]);
444   SelectObject (aWorkDC, Draw_colorPenTab[myCurrPen]);
445   SetBkColor   (aWorkDC, RGB(0, 0, 0));
446   SelectObject (aWorkDC, GetStockObject(BLACK_BRUSH));
447   SetTextColor (aWorkDC, RGB(0,0,255));
448   return aWorkDC;
449 }
450 
451 //=======================================================================
452 //function : releaseMemDC
453 //purpose  :
454 //=======================================================================
releaseMemDC(HDC theMemDC)455 void Draw_Window::releaseMemDC (HDC theMemDC)
456 {
457   if (!myUseBuffer || !theMemDC)
458   {
459     return;
460   }
461 
462   if (myOldHbm)
463   {
464     SelectObject (theMemDC, myOldHbm);
465   }
466   DeleteDC (theMemDC);
467 }
468 #endif
469 
470 //=======================================================================
471 //function : InitBuffer
472 //purpose  :
473 //=======================================================================
InitBuffer()474 void Draw_Window::InitBuffer()
475 {
476 #ifdef _WIN32
477   if (myUseBuffer)
478   {
479     RECT aRect;
480     HDC hDC = GetDC (myWindow);
481     GetClientRect (myWindow, &aRect);
482     if (myMemHbm)
483     {
484       BITMAP aBmp;
485       GetObjectW (myMemHbm, sizeof(BITMAP), &aBmp);
486       if ((aRect.right - aRect.left) == aBmp.bmWidth
487        && (aRect.bottom - aRect.top) == aBmp.bmHeight)
488       {
489         return;
490       }
491       DeleteObject (myMemHbm);
492     }
493     myMemHbm = (HBITMAP )CreateCompatibleBitmap (hDC,
494                                                  aRect.right - aRect.left,
495                                                  aRect.bottom - aRect.top);
496     HDC aMemDC = getMemDC (hDC);
497     FillRect (aMemDC, &aRect, (HBRUSH)GetStockObject(BLACK_BRUSH));
498     releaseMemDC (aMemDC);
499     ReleaseDC (myWindow, hDC);
500   }
501   else
502   {
503     if (myMemHbm)
504     {
505       DeleteObject (myMemHbm);
506       myMemHbm = NULL;
507     }
508   }
509 #elif defined(HAVE_XLIB)
510   if (myUseBuffer)
511   {
512     if (myImageBuffer != 0)
513     {
514       XFreePixmap (Draw_WindowDisplay, myImageBuffer);
515     }
516     XWindowAttributes aWinAttr;
517     XGetWindowAttributes (Draw_WindowDisplay, myWindow, &aWinAttr);
518     myImageBuffer = XCreatePixmap (Draw_WindowDisplay, myWindow, aWinAttr.width, aWinAttr.height, aWinAttr.depth);
519   }
520   else if (myImageBuffer != 0)
521   {
522     XFreePixmap (Draw_WindowDisplay, myImageBuffer);
523     myImageBuffer = 0;
524   }
525 #endif
526 }
527 
528 //=======================================================================
529 //function : SetPosition
530 //purpose  :
531 //=======================================================================
SetPosition(Standard_Integer theNewXpos,Standard_Integer theNewYpos)532 void Draw_Window::SetPosition (Standard_Integer theNewXpos,
533                                Standard_Integer theNewYpos)
534 {
535 #ifdef _WIN32
536   UINT aFlags = SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER;
537   if (Draw_VirtualWindows)
538   {
539     aFlags |= SWP_NOSENDCHANGING;
540   }
541   SetWindowPos (myWindow, 0, theNewXpos, theNewYpos, 0, 0, aFlags);
542 #elif defined(HAVE_XLIB)
543   Standard_Integer aPosX = 0, aPosY = 0;
544   GetPosition (aPosX, aPosY);
545   if (aPosX != theNewXpos
546    || aPosY != theNewYpos)
547   {
548     XMoveWindow (Draw_WindowDisplay, myWindow, theNewXpos, theNewYpos);
549   }
550 #else
551   (void )theNewXpos;
552   (void )theNewYpos;
553 #endif
554 }
555 
556 //=======================================================================
557 //function : SetDimension
558 //purpose  :
559 //=======================================================================
SetDimension(Standard_Integer theNewDx,Standard_Integer theNewDy)560 void Draw_Window::SetDimension (Standard_Integer theNewDx,
561                                 Standard_Integer theNewDy)
562 {
563 #ifdef _WIN32
564   UINT aFlags = SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER;
565   if (Draw_VirtualWindows)
566   {
567     aFlags |= SWP_NOSENDCHANGING;
568   }
569   SetWindowPos (myWindow, 0, 0, 0, theNewDx, theNewDy, aFlags);
570 #elif defined(HAVE_XLIB)
571   if (theNewDx != WidthWin()
572    || theNewDy != HeightWin())
573   {
574     XResizeWindow (Draw_WindowDisplay, myWindow, theNewDx, theNewDy);
575   }
576 #else
577   (void )theNewDx;
578   (void )theNewDy;
579 #endif
580 }
581 
582 //=======================================================================
583 //function : GetPosition
584 //purpose  :
585 //=======================================================================
GetPosition(Standard_Integer & thePosX,Standard_Integer & thePosY)586 void Draw_Window::GetPosition (Standard_Integer& thePosX,
587                                Standard_Integer& thePosY)
588 {
589   thePosX = thePosY = 0;
590 #ifdef _WIN32
591   RECT aRect;
592   GetWindowRect (myWindow, &aRect);
593 
594   POINT aPoint;
595   aPoint.x = aRect.left;
596   aPoint.y = aRect.top;
597 
598   ScreenToClient (hWndClientMDI, &aPoint);
599   thePosX = aPoint.x;
600   thePosY = aPoint.y;
601 #elif defined(HAVE_XLIB)
602   XWindowAttributes aWinAttr;
603   XGetWindowAttributes (Draw_WindowDisplay, myWindow, &aWinAttr);
604   thePosX = aWinAttr.x;
605   thePosY = aWinAttr.y;
606 #endif
607 }
608 
609 //=======================================================================
610 //function : HeightWin
611 //purpose  :
612 //=======================================================================
HeightWin() const613 Standard_Integer Draw_Window::HeightWin() const
614 {
615 #ifdef _WIN32
616   RECT aRect;
617   GetClientRect (myWindow, &aRect);
618   return aRect.bottom - aRect.top;
619 #elif defined(HAVE_XLIB)
620   XWindowAttributes aWinAttr;
621   XGetWindowAttributes (Draw_WindowDisplay, myWindow, &aWinAttr);
622   return aWinAttr.height;
623 #else
624   return 1;
625 #endif
626 }
627 
628 //=======================================================================
629 //function : WidthWin
630 //purpose  :
631 //=======================================================================
WidthWin() const632 Standard_Integer Draw_Window::WidthWin() const
633 {
634 #ifdef _WIN32
635   RECT aRect;
636   GetClientRect (myWindow, &aRect);
637   return aRect.right - aRect.left;
638 #elif defined(HAVE_XLIB)
639   XWindowAttributes aWinAttr;
640   XGetWindowAttributes (Draw_WindowDisplay, myWindow, &aWinAttr);
641   return aWinAttr.width;
642 #else
643   return 1;
644 #endif
645 }
646 
647 //=======================================================================
648 //function : SetTitle
649 //purpose  :
650 //=======================================================================
SetTitle(const TCollection_AsciiString & theTitle)651 void Draw_Window::SetTitle (const TCollection_AsciiString& theTitle)
652 {
653 #ifdef _WIN32
654   const TCollection_ExtendedString aTitleW (theTitle);
655   SetWindowTextW (myWindow, aTitleW.ToWideString());
656 #elif defined(HAVE_XLIB)
657   XStoreName (Draw_WindowDisplay, myWindow, theTitle.ToCString());
658 #else
659   (void )theTitle;
660 #endif
661 }
662 
663 //=======================================================================
664 //function : GetTitle
665 //purpose  :
666 //=======================================================================
GetTitle() const667 TCollection_AsciiString Draw_Window::GetTitle() const
668 {
669 #ifdef _WIN32
670   wchar_t aTitleW[32];
671   GetWindowTextW (myWindow, aTitleW, 30);
672   return TCollection_AsciiString (aTitleW);
673 #elif defined(HAVE_XLIB)
674   char* aTitle = NULL;
675   XFetchName (Draw_WindowDisplay, myWindow, &aTitle);
676   return TCollection_AsciiString (aTitle);
677 #else
678   return TCollection_AsciiString();
679 #endif
680 }
681 
682 //=======================================================================
683 //function :DefineColor
684 //purpose  :
685 //=======================================================================
DefineColor(const Standard_Integer theIndex,const char * theColorName)686 Standard_Boolean Draw_Window::DefineColor (const Standard_Integer theIndex,
687                                            const char* theColorName)
688 {
689 #if defined(HAVE_XLIB)
690   XColor aColor;
691   if (!XParseColor (Draw_WindowDisplay, Draw_WindowColorMap, theColorName, &aColor))
692   {
693     return Standard_False;
694   }
695   if (!XAllocColor (Draw_WindowDisplay, Draw_WindowColorMap, &aColor))
696   {
697     return Standard_False;
698   }
699   thePixels[theIndex % MAXCOLOR] = aColor.pixel;
700   return Standard_True;
701 #else
702   (void )theIndex;
703   (void )theColorName;
704   return Standard_True;
705 #endif
706 }
707 
708 //=======================================================================
709 //function : IsMapped
710 //purpose  :
711 //=======================================================================
IsMapped() const712 bool Draw_Window::IsMapped() const
713 {
714   if (Draw_VirtualWindows
715    || myWindow == 0)
716   {
717     return false;
718   }
719 
720 #ifdef _WIN32
721   LONG aWinStyle = GetWindowLongW (myWindow, GWL_STYLE);
722   return (aWinStyle & WS_VISIBLE)  != 0
723       && (aWinStyle & WS_MINIMIZE) == 0;
724 #elif defined(HAVE_XLIB)
725   XFlush (Draw_WindowDisplay);
726   XWindowAttributes aWinAttr;
727   XGetWindowAttributes (Draw_WindowDisplay, myWindow, &aWinAttr);
728   return aWinAttr.map_state == IsUnviewable
729       || aWinAttr.map_state == IsViewable;
730 #else
731   return false;
732 #endif
733 }
734 
735 //=======================================================================
736 //function : DisplayWindow
737 //purpose  :
738 //=======================================================================
DisplayWindow()739 void Draw_Window::DisplayWindow()
740 {
741   if (Draw_VirtualWindows)
742   {
743     return;
744   }
745 
746 #ifdef _WIN32
747   ShowWindow (myWindow, SW_SHOW);
748   UpdateWindow (myWindow);
749 #elif defined(HAVE_XLIB)
750   XMapRaised (Draw_WindowDisplay, myWindow);
751   XFlush (Draw_WindowDisplay);
752 #endif
753 }
754 
755 //=======================================================================
756 //function : Hide
757 //purpose  :
758 //=======================================================================
Hide()759 void Draw_Window::Hide()
760 {
761 #ifdef _WIN32
762   ShowWindow (myWindow, SW_HIDE);
763 #elif defined(HAVE_XLIB)
764   XUnmapWindow (Draw_WindowDisplay, myWindow);
765 #endif
766 }
767 
768 //=======================================================================
769 //function : Destroy
770 //purpose  :
771 //=======================================================================
Destroy()772 void Draw_Window::Destroy()
773 {
774 #ifdef _WIN32
775   DestroyWindow (myWindow);
776 #elif defined(HAVE_XLIB)
777   XFreeGC (Draw_WindowDisplay, myBase->gc);
778   XDestroyWindow (Draw_WindowDisplay, myWindow);
779   myWindow = 0;
780   if (myImageBuffer != 0)
781   {
782     XFreePixmap (Draw_WindowDisplay, myImageBuffer);
783     myImageBuffer = 0;
784   }
785 #endif
786 }
787 
788 //=======================================================================
789 //function : Clear
790 //purpose  :
791 //=======================================================================
Clear()792 void Draw_Window::Clear()
793 {
794 #ifdef _WIN32
795   HDC hDC = GetDC (myWindow);
796   HDC aWorkDC = myUseBuffer ? getMemDC(hDC) : hDC;
797 
798   SaveDC (aWorkDC);
799   SelectObject (aWorkDC, GetStockObject(BLACK_PEN));
800   Rectangle (aWorkDC, 0, 0, WidthWin(), HeightWin());
801   RestoreDC (aWorkDC,-1);
802 
803   if (myUseBuffer)
804   {
805     releaseMemDC (aWorkDC);
806   }
807   ReleaseDC (myWindow, hDC);
808 #elif defined(HAVE_XLIB)
809   if (myUseBuffer)
810   {
811     // XClearArea only applicable for windows
812     XGCValues aCurrValues;
813     XGetGCValues (Draw_WindowDisplay, myBase->gc, GCBackground | GCForeground, &aCurrValues);
814     XSetForeground (Draw_WindowDisplay, myBase->gc, aCurrValues.background);
815     XFillRectangle (Draw_WindowDisplay, myImageBuffer, myBase->gc, 0, 0, WidthWin(), HeightWin());
816     XSetForeground (Draw_WindowDisplay, myBase->gc, aCurrValues.foreground);
817   }
818   else
819   {
820     XClearArea (Draw_WindowDisplay, myWindow, 0, 0, 0, 0, False);
821   }
822 #endif
823 }
824 
825 //=======================================================================
826 //function : Flush
827 //purpose  :
828 //=======================================================================
Flush()829 void Draw_Window::Flush()
830 {
831 #if defined(HAVE_XLIB)
832   XFlush (Draw_WindowDisplay);
833 #endif
834 }
835 
836 //=======================================================================
837 //function : DrawString
838 //purpose  :
839 //=======================================================================
DrawString(Standard_Integer theX,Standard_Integer theY,const char * theText)840 void Draw_Window::DrawString (Standard_Integer theX, Standard_Integer theY,
841                               const char* theText)
842 {
843 #ifdef _WIN32
844   HDC hDC = GetDC (myWindow);
845   HDC aWorkDC = myUseBuffer ? getMemDC(hDC) : hDC;
846 
847   const TCollection_ExtendedString aTextW (theText);
848   TextOutW (aWorkDC, theX, theY, aTextW.ToWideString(), aTextW.Length());
849 
850   if (myUseBuffer)
851   {
852     releaseMemDC (aWorkDC);
853   }
854   ReleaseDC (myWindow, hDC);
855 #elif defined(HAVE_XLIB)
856   XDrawString (Draw_WindowDisplay, GetDrawable(), myBase->gc, theX, theY, (char* )theText, strlen(theText));
857 #else
858   (void )theX;
859   (void )theY;
860   (void )theText;
861 #endif
862 }
863 
864 //=======================================================================
865 //function : DrawSegments
866 //purpose  :
867 //=======================================================================
DrawSegments(const Draw_XSegment * theSegments,Standard_Integer theNbElems)868 void Draw_Window::DrawSegments (const Draw_XSegment* theSegments,
869                                 Standard_Integer theNbElems)
870 {
871 #ifdef _WIN32
872   HDC hDC = GetDC (myWindow);
873   HDC aWorkDC = myUseBuffer ? getMemDC(hDC) : hDC;
874   for (int aSegIter = 0; aSegIter < theNbElems; ++aSegIter)
875   {
876     const Draw_XSegment& aSeg = theSegments[aSegIter];
877     MoveToEx(aWorkDC, aSeg[0].x(), aSeg[0].y(), NULL);
878     LineTo  (aWorkDC, aSeg[1].x(), aSeg[1].y());
879   }
880   if (myUseBuffer)
881   {
882     releaseMemDC (aWorkDC);
883   }
884   ReleaseDC (myWindow, hDC);
885 #elif defined(HAVE_XLIB)
886   Standard_STATIC_ASSERT(sizeof(Draw_XSegment) == sizeof(XSegment));
887   XDrawSegments (Draw_WindowDisplay, GetDrawable(), myBase->gc, (XSegment* )theSegments, theNbElems);
888 #else
889   (void )theSegments;
890   (void )theNbElems;
891 #endif
892 }
893 
894 //=======================================================================
895 //function : Redraw
896 //purpose  :
897 //=======================================================================
Redraw()898 void Draw_Window::Redraw()
899 {
900 #ifdef _WIN32
901   if (myUseBuffer)
902   {
903     HDC hDC = GetDC (myWindow);
904     RECT aRect;
905     GetClientRect (myWindow, &aRect);
906     HDC aMemDC = getMemDC (hDC);
907     BitBlt (hDC,
908             aRect.left, aRect.top,
909             aRect.right - aRect.left, aRect.bottom - aRect.top,
910             aMemDC,
911             0, 0, SRCCOPY);
912     releaseMemDC (aMemDC);
913     ReleaseDC (myWindow, hDC);
914   }
915 #elif defined(HAVE_XLIB)
916   if (myUseBuffer)
917   {
918     XCopyArea (Draw_WindowDisplay,
919                myImageBuffer, myWindow,
920                myBase->gc,
921                0, 0,
922                WidthWin(), HeightWin(),
923                0, 0);
924   }
925 #endif
926 }
927 
928 //=======================================================================
929 //function : SetColor
930 //purpose  :
931 //=======================================================================
SetColor(Standard_Integer theColor)932 void Draw_Window::SetColor (Standard_Integer theColor)
933 {
934 #ifdef _WIN32
935   HDC hDC = GetDC (myWindow);
936   myCurrPen = theColor;
937   SelectObject (hDC, Draw_colorPenTab[theColor]);
938   ReleaseDC (myWindow, hDC);
939 #elif defined(HAVE_XLIB)
940   XSetForeground (Draw_WindowDisplay, myBase->gc, thePixels[theColor]);
941 #endif
942   myCurrentColor = theColor;
943 }
944 
945 //=======================================================================
946 //function : SetMode
947 //purpose  :
948 //=======================================================================
SetMode(int theMode)949 void Draw_Window::SetMode (int theMode)
950 {
951 #ifdef _WIN32
952   HDC hDC = GetDC (myWindow);
953   myCurrMode = theMode;
954   SetROP2 (hDC, Draw_modeTab[theMode]);
955   ReleaseDC (myWindow, hDC);
956 #elif defined(HAVE_XLIB)
957   XSetFunction (Draw_WindowDisplay, myBase->gc, theMode);
958 #else
959   (void )theMode;
960 #endif
961 }
962 
963 #ifdef _WIN32
964 /*--------------------------------------------------------*\
965 |  SaveBitmap
966 \*--------------------------------------------------------*/
SaveBitmap(HBITMAP theHBitmap,const char * theFileName)967 static Standard_Boolean SaveBitmap (HBITMAP     theHBitmap,
968                                     const char* theFileName)
969 {
970   // Get information about the bitmap
971   BITMAP aBitmap;
972   if (GetObjectW (theHBitmap, sizeof(BITMAP), &aBitmap) == 0)
973   {
974     return Standard_False;
975   }
976 
977   Image_AlienPixMap anImage;
978   const Standard_Size aSizeRowBytes = ((Standard_Size(aBitmap.bmWidth) * 24 + 31) / 32) * 4; // 4 bytes alignment for GetDIBits()
979   if (!anImage.InitTrash (Image_Format_BGR, Standard_Size(aBitmap.bmWidth), Standard_Size(aBitmap.bmHeight), aSizeRowBytes))
980   {
981     return Standard_False;
982   }
983   anImage.SetTopDown (false);
984 
985   // Setup image data
986   BITMAPINFOHEADER aBitmapInfo;
987   memset (&aBitmapInfo, 0, sizeof(BITMAPINFOHEADER));
988   aBitmapInfo.biSize        = sizeof(BITMAPINFOHEADER);
989   aBitmapInfo.biWidth       = aBitmap.bmWidth;
990   aBitmapInfo.biHeight      = aBitmap.bmHeight; // positive means bottom-up!
991   aBitmapInfo.biPlanes      = 1;
992   aBitmapInfo.biBitCount    = 24;
993   aBitmapInfo.biCompression = BI_RGB;
994 
995   // Copy the pixels
996   HDC aDC = GetDC (NULL);
997   Standard_Boolean isSuccess = GetDIBits (aDC, theHBitmap,
998                                           0,                           // first scan line to set
999                                           aBitmap.bmHeight,            // number of scan lines to copy
1000                                           anImage.ChangeData(),        // array for bitmap bits
1001                                           (LPBITMAPINFO )&aBitmapInfo, // bitmap data info
1002                                           DIB_RGB_COLORS) != 0;
1003   ReleaseDC (NULL, aDC);
1004   return isSuccess && anImage.Save (theFileName);
1005 }
1006 #endif
1007 
1008 //=======================================================================
1009 //function : Save
1010 //purpose  :
1011 //=======================================================================
Save(const char * theFileName) const1012 Standard_Boolean Draw_Window::Save (const char* theFileName) const
1013 {
1014 #ifdef _WIN32
1015   if (myUseBuffer)
1016   {
1017     return SaveBitmap (myMemHbm, theFileName);
1018   }
1019 
1020   RECT aRect;
1021   GetClientRect (myWindow, &aRect);
1022   int aWidth  = aRect.right  - aRect.left;
1023   int aHeight = aRect.bottom - aRect.top;
1024 
1025   // Prepare the DCs
1026   HDC aDstDC = GetDC (NULL);
1027   HDC aSrcDC = GetDC (myWindow); // we copy only client area
1028   HDC aMemDC = CreateCompatibleDC (aDstDC);
1029 
1030   // Copy the screen to the bitmap
1031   HBITMAP anHBitmapDump = CreateCompatibleBitmap (aDstDC, aWidth, aHeight);
1032   HBITMAP anHBitmapOld = (HBITMAP )SelectObject (aMemDC, anHBitmapDump);
1033   BitBlt (aMemDC, 0, 0, aWidth, aHeight, aSrcDC, 0, 0, SRCCOPY);
1034 
1035   Standard_Boolean isSuccess = SaveBitmap (anHBitmapDump, theFileName);
1036 
1037   // Free objects
1038   DeleteObject (SelectObject (aMemDC, anHBitmapOld));
1039   DeleteDC (aMemDC);
1040 
1041   return isSuccess;
1042 #elif defined(HAVE_XLIB)
1043   // make sure all draw operations done
1044   XSync (Draw_WindowDisplay, True);
1045 
1046   // the attributes
1047   XWindowAttributes aWinAttr;
1048   XGetWindowAttributes (Draw_WindowDisplay, myWindow, &aWinAttr);
1049 
1050   if (!myUseBuffer)
1051   {
1052     // make sure that the whole window fit on display to prevent BadMatch error
1053     XWindowAttributes aWinAttrRoot;
1054     XGetWindowAttributes (Draw_WindowDisplay, XRootWindowOfScreen (aWinAttr.screen), &aWinAttrRoot);
1055 
1056     Window aWinChildDummy;
1057     int aWinLeft = 0, aWinTop = 0;
1058     XTranslateCoordinates (Draw_WindowDisplay, myWindow, XRootWindowOfScreen (aWinAttr.screen),
1059                            0, 0, &aWinLeft, &aWinTop, &aWinChildDummy);
1060 
1061     if (((aWinLeft + aWinAttr.width) > aWinAttrRoot.width)  || aWinLeft < aWinAttrRoot.x
1062      || ((aWinTop + aWinAttr.height) > aWinAttrRoot.height) || aWinTop  < aWinAttrRoot.y)
1063     {
1064       std::cerr << "The window not fully visible! Can't create the snapshot.\n";
1065       return Standard_False;
1066     }
1067   }
1068 
1069   XVisualInfo aVInfo;
1070   if (XMatchVisualInfo (Draw_WindowDisplay, Draw_WindowScreen, 32, TrueColor, &aVInfo) == 0
1071    && XMatchVisualInfo (Draw_WindowDisplay, Draw_WindowScreen, 24, TrueColor, &aVInfo) == 0)
1072   {
1073     std::cerr << "24-bit TrueColor visual is not supported by server!\n";
1074     return Standard_False;
1075   }
1076 
1077   Image_AlienPixMap anImage;
1078   bool isBigEndian = Image_PixMap::IsBigEndianHost();
1079   const Standard_Size aSizeRowBytes = Standard_Size(aWinAttr.width) * 4;
1080   if (!anImage.InitTrash (isBigEndian ? Image_Format_RGB32 : Image_Format_BGR32,
1081                           Standard_Size(aWinAttr.width), Standard_Size(aWinAttr.height), aSizeRowBytes))
1082   {
1083     return Standard_False;
1084   }
1085   anImage.SetTopDown (true);
1086 
1087   XImage* anXImage = XCreateImage (Draw_WindowDisplay, aVInfo.visual,
1088                                    32, ZPixmap, 0, (char* )anImage.ChangeData(), aWinAttr.width, aWinAttr.height, 32, int(aSizeRowBytes));
1089   anXImage->bitmap_bit_order = anXImage->byte_order = (isBigEndian ? MSBFirst : LSBFirst);
1090   if (XGetSubImage (Draw_WindowDisplay, GetDrawable(),
1091                     0, 0, aWinAttr.width, aWinAttr.height,
1092                     AllPlanes, ZPixmap, anXImage, 0, 0) == NULL)
1093   {
1094     anXImage->data = NULL;
1095     XDestroyImage (anXImage);
1096     return Standard_False;
1097   }
1098 
1099   // destroy the image
1100   anXImage->data = NULL;
1101   XDestroyImage (anXImage);
1102 
1103   // save the image
1104   return anImage.Save (theFileName);
1105 #else
1106   (void )theFileName;
1107   return false;
1108 #endif
1109 }
1110 
1111 #endif // !__APPLE__
1112 
1113 #if defined(HAVE_XLIB)
1114 //=======================================================================
1115 //function : Wait
1116 //purpose  :
1117 //=======================================================================
Wait(Standard_Boolean theToWait)1118 void Draw_Window::Wait (Standard_Boolean theToWait)
1119 {
1120   Flush();
1121   long aMask = ButtonPressMask | ExposureMask | StructureNotifyMask;
1122   if (!theToWait) { aMask |= PointerMotionMask; }
1123   XSelectInput (Draw_WindowDisplay, myWindow, aMask);
1124 }
1125 
1126 //! Process pending X events.
processXEvents(ClientData,int)1127 static void processXEvents (ClientData , int )
1128 {
1129   // test for X Event
1130   while (XPending (Draw_WindowDisplay))
1131   {
1132     XEvent anEvent;
1133     XNextEvent (Draw_WindowDisplay, &anEvent);
1134 
1135     // search the window in the window list
1136     bool isFound = false;
1137 
1138     for (NCollection_List<Draw_Window*>::Iterator aWinIter (getDrawWindowList());
1139          aWinIter.More(); aWinIter.Next())
1140     {
1141       Draw_Window* aDrawWin = aWinIter.Value();
1142       if (aDrawWin->IsEqualWindows (anEvent.xany.window))
1143       {
1144         switch (anEvent.type)
1145         {
1146           case ClientMessage:
1147           {
1148             if (anEvent.xclient.data.l[0] == (int )Draw_DisplayConnection->GetAtom (Aspect_XA_DELETE_WINDOW))
1149             {
1150               aDrawWin->Hide(); // just hide the window
1151             }
1152             break;
1153           }
1154           case Expose:
1155           {
1156             aDrawWin->WExpose();
1157             break;
1158           }
1159         }
1160 
1161         isFound = true;
1162         break;
1163       }
1164     }
1165     if (!isFound)
1166     {
1167     #ifdef _TK
1168       Tk_HandleEvent (&anEvent);
1169     #endif
1170     }
1171   }
1172 }
1173 
1174 //======================================================
1175 // function : GetNextEvent()
1176 // purpose :
1177 //======================================================
GetNextEvent(Draw_Window::Draw_XEvent & theEvent)1178 void Draw_Window::GetNextEvent (Draw_Window::Draw_XEvent& theEvent)
1179 {
1180   XEvent anXEvent;
1181   XNextEvent (Draw_WindowDisplay, &anXEvent);
1182   switch (anXEvent.type)
1183   {
1184     case ButtonPress:
1185     {
1186       theEvent.type = 4;
1187       theEvent.window = anXEvent.xbutton.window;
1188       theEvent.button = anXEvent.xbutton.button;
1189       theEvent.x = anXEvent.xbutton.x;
1190       theEvent.y = anXEvent.xbutton.y;
1191       break;
1192     }
1193     case MotionNotify:
1194     {
1195       theEvent.type = 6;
1196       theEvent.window = anXEvent.xmotion.window;
1197       theEvent.button = 0;
1198       theEvent.x = anXEvent.xmotion.x;
1199       theEvent.y = anXEvent.xmotion.y;
1200       break;
1201     }
1202   }
1203 }
1204 #endif
1205 
1206 #ifndef _WIN32
1207 //======================================================
1208 // function :Run_Appli
1209 // purpose :
1210 //======================================================
1211 static Standard_Boolean(*Interprete) (const char*);
1212 
Run_Appli(Standard_Boolean (* interprete)(const char *))1213 void Run_Appli(Standard_Boolean (*interprete) (const char*))
1214 {
1215   Interprete = interprete;
1216 
1217   bool toWaitInput = true;
1218 #ifdef __EMSCRIPTEN__
1219   toWaitInput = !occJSModuleNoExitRuntime();
1220 #endif
1221 
1222   // Commands will come from standard input, so set up an event handler for standard input.
1223   // If the input device is aEvaluate the .rc file, if one has been specified,
1224   // set up an event handler for standard input, and print a prompt if the input device is a terminal.
1225   Tcl_Channel anInChannel = Tcl_GetStdChannel(TCL_STDIN);
1226   if (anInChannel && toWaitInput)
1227   {
1228     Tcl_CreateChannelHandler (anInChannel, TCL_READABLE, StdinProc, (ClientData )anInChannel);
1229   }
1230 
1231   // Create a handler for the draw display
1232 #if defined(HAVE_XLIB)
1233   Tcl_CreateFileHandler (ConnectionNumber(Draw_WindowDisplay), TCL_READABLE, processXEvents, (ClientData) 0);
1234 #endif // __APPLE__
1235 
1236   Draw_Interpretor& aCommands = Draw::GetInterpretor();
1237 
1238   if (tty) { Prompt (aCommands.Interp(), 0); }
1239   Prompt (aCommands.Interp(), 0);
1240 
1241   Tcl_Channel anOutChannel = Tcl_GetStdChannel(TCL_STDOUT);
1242   if (anOutChannel)
1243   {
1244     Tcl_Flush (anOutChannel);
1245   }
1246   Tcl_DStringInit (&Draw_TclCommand);
1247 
1248 #ifdef _TK
1249   if (Draw_VirtualWindows)
1250   {
1251     // main window will never shown
1252     // but main loop will parse all Xlib messages
1253     Tcl_Eval(aCommands.Interp(), "wm withdraw .");
1254   }
1255   // Loop infinitely, waiting for commands to execute.
1256   // When there are no windows left, Tk_MainLoop returns and we exit.
1257   Tk_MainLoop();
1258 #else
1259   if (!toWaitInput)
1260   {
1261     return;
1262   }
1263 
1264   for (;;)
1265   {
1266     Tcl_DoOneEvent (0); // practically the same as Tk_MainLoop()
1267   }
1268 #endif
1269 
1270   for (NCollection_List<Draw_Window::FCallbackBeforeTerminate>::Iterator anIter (TermCallbacks());
1271        anIter.More(); anIter.Next())
1272   {
1273     (*anIter.Value())();
1274   }
1275 }
1276 
1277 //======================================================
1278 // function : Init_Appli()
1279 // purpose  :
1280 //======================================================
Init_Appli()1281 Standard_Boolean Init_Appli()
1282 {
1283   Draw_Interpretor& aCommands = Draw::GetInterpretor();
1284   aCommands.Init();
1285   Tcl_Interp *interp = aCommands.Interp();
1286   Tcl_Init (interp);
1287 
1288 #ifdef _TK
1289   try
1290   {
1291     OCC_CATCH_SIGNALS
1292     Tk_Init (interp);
1293   }
1294   catch (Standard_Failure const& theFail)
1295   {
1296     Message::SendFail() << "TK_Init() failed with " << theFail;
1297   }
1298 
1299   Tcl_StaticPackage(interp, "Tk", Tk_Init, (Tcl_PackageInitProc *) NULL);
1300 
1301   Tk_Window aMainWindow = Tk_MainWindow(interp) ;
1302   if (aMainWindow == NULL) {
1303 #if ((TCL_MAJOR_VERSION > 8) || ((TCL_MAJOR_VERSION == 8) && (TCL_MINOR_VERSION >= 5)))
1304     fprintf(stderr, "%s\n", Tcl_GetStringResult(interp));
1305 #else
1306     fprintf(stderr, "%s\n", interp->result);
1307 #endif
1308     exit(1);
1309   }
1310 #if defined(__APPLE__) && !defined(HAVE_XLIB)
1311   Tk_SetAppName(aMainWindow, "Draw");
1312 #else
1313   Tk_Name(aMainWindow) = Tk_GetUid(Tk_SetAppName(aMainWindow, "Draw"));
1314 #endif
1315 
1316   Tk_GeometryRequest (aMainWindow, 200, 200);
1317 #endif
1318 
1319 #if defined(HAVE_XLIB)
1320   if (Draw_DisplayConnection.IsNull())
1321   {
1322     try
1323     {
1324       Draw_DisplayConnection = new Aspect_DisplayConnection();
1325     }
1326     catch (Standard_Failure const& theFail)
1327     {
1328       std::cout << "Cannot open display (" << theFail << "). Interpret commands in batch mode." << std::endl;
1329       return Standard_False;
1330     }
1331   }
1332   if (Draw_WindowDisplay == NULL)
1333   {
1334     Draw_WindowDisplay = (Display* )Draw_DisplayConnection->GetDisplayAspect();
1335   }
1336   //
1337   // synchronize the display server : could be done within Tk_Init
1338   //
1339   XSynchronize (Draw_WindowDisplay, True);
1340   XSetInputFocus (Draw_WindowDisplay,
1341                   PointerRoot,
1342                   RevertToPointerRoot,
1343                   CurrentTime);
1344 
1345   Draw_WindowScreen   = DefaultScreen(Draw_WindowDisplay);
1346   Draw_WindowColorMap = DefaultColormap(Draw_WindowDisplay,
1347                                         Draw_WindowScreen);
1348 #endif // __APPLE__
1349 
1350   tty = isatty(0);
1351   Tcl_SetVar(interp,"tcl_interactive",(char*)(tty ? "1" : "0"), TCL_GLOBAL_ONLY);
1352 //  Tcl_SetVar(interp,"tcl_interactive",tty ? "1" : "0", TCL_GLOBAL_ONLY);
1353   return Standard_True;
1354 }
1355 
1356 //======================================================
1357 // function : Destroy_Appli()
1358 // purpose  :
1359 //======================================================
Destroy_Appli()1360 void Destroy_Appli()
1361 {
1362   //XCloseDisplay(Draw_WindowDisplay);
1363 }
1364 
1365 //! This procedure is invoked by the event dispatcher whenever standard input becomes readable.
1366 //! It grabs the next line of input characters, adds them to a command being assembled,
1367 //! and executes the command if it's complete.
1368 //! Side effects: Could be almost arbitrary, depending on the command that's typed.
StdinProc(ClientData clientData,int theMask)1369 static void StdinProc (ClientData clientData, int theMask)
1370 {
1371   (void )theMask;
1372   static int gotPartial = 0;
1373 //  int code, count;
1374   Tcl_Channel chan = (Tcl_Channel) clientData;
1375 
1376   // MSV Nov 2, 2001: patch for TCL 8.3: initialize line to avoid exception
1377   //                  when first user input is an empty string
1378   Tcl_DStringFree (&Draw_TclLine);
1379   int count = Tcl_Gets(chan, &Draw_TclLine);
1380 
1381   // MKV 26.05.05
1382 #if ((TCL_MAJOR_VERSION > 8) || ((TCL_MAJOR_VERSION == 8) && (TCL_MINOR_VERSION >= 4)))
1383   Tcl_DString aLineTmp;
1384   Tcl_DStringInit (&aLineTmp);
1385   Tcl_UniChar* aUniCharString = Tcl_UtfToUniCharDString (Tcl_DStringValue (&Draw_TclLine), -1, &aLineTmp);
1386   Standard_Integer l = Tcl_UniCharLen (aUniCharString);
1387   TCollection_AsciiString anAsciiString;
1388   for (Standard_Integer i = 0; i < l; ++i)
1389   {
1390     Standard_Character aCharacter = aUniCharString[i];
1391     anAsciiString.AssignCat (aCharacter);
1392   }
1393   Tcl_DStringInit (&Draw_TclLine);
1394   Tcl_DStringAppend (&Draw_TclLine, anAsciiString.ToCString(), -1);
1395 #endif
1396   if (count < 0)
1397   {
1398     if (!gotPartial)
1399     {
1400       if (tty)
1401       {
1402         Tcl_Exit(0);
1403       }
1404       else
1405       {
1406         Tcl_DeleteChannelHandler(chan, StdinProc, (ClientData) chan);
1407       }
1408       return;
1409     }
1410     else
1411     {
1412       count = 0;
1413     }
1414   }
1415 
1416   (void) Tcl_DStringAppend (&Draw_TclCommand, Tcl_DStringValue (&Draw_TclLine), -1);
1417   char* cmd = Tcl_DStringAppend (&Draw_TclCommand, "\n", -1);
1418   Tcl_DStringFree (&Draw_TclLine);
1419   try
1420   {
1421     OCC_CATCH_SIGNALS
1422     if (!Tcl_CommandComplete (cmd))
1423     {
1424       gotPartial = 1;
1425       goto prompt;
1426     }
1427     gotPartial = 0;
1428 
1429     /*
1430      * Disable the stdin channel handler while evaluating the command;
1431      * otherwise if the command re-enters the event loop we might
1432      * process commands from stdin before the current command is finished.
1433      * Among other things, this will trash the text of the command being evaluated.
1434      */
1435     Tcl_CreateChannelHandler(chan, 0, StdinProc, (ClientData) chan);
1436 
1437     /*
1438      * Disable the stdin file handler while evaluating the command;
1439      * otherwise if the command re-enters the event loop we might
1440      * process commands from stdin before the current command is finished.
1441      * Among other things, this will trash the text of the command being evaluated.
1442      */
1443 
1444 #ifdef _TK
1445     // Tk_CreateFileHandler (0, 0, StdinProc, (ClientData) 0);
1446 #endif
1447     // xab average to avoid an output SIGBUS of DRAW
1448     // to ultimately precise or remove once
1449     // the problem of free on the global variable at the average
1450     //
1451     Interprete (cmd);
1452 
1453     Tcl_CreateChannelHandler (chan, TCL_READABLE, StdinProc, (ClientData) chan);
1454     Tcl_DStringFree (&Draw_TclCommand);
1455 
1456   /*
1457    * Output a prompt.
1458    */
1459 prompt:
1460     if (tty)
1461     {
1462       Prompt (Draw::GetInterpretor().Interp(), gotPartial);
1463     }
1464 
1465   } catch (Standard_Failure const&) {}
1466 }
1467 
1468 #else
1469 
1470 // Source Specifique WNT
1471 
1472 /*--------------------------------------------------------*\
1473 |  CREATE DRAW WINDOW PROCEDURE
1474 \*--------------------------------------------------------*/
createDrawWindow(HWND hWndClient,int nitem)1475 HWND Draw_Window::createDrawWindow (HWND hWndClient, int nitem)
1476 {
1477   if (Draw_IsConsoleSubsystem)
1478   {
1479     HWND aWin = CreateWindowW (DRAWCLASS, DRAWTITLE,
1480                                WS_OVERLAPPEDWINDOW,
1481                                1,1,1,1,
1482                                NULL, NULL,::GetModuleHandle(NULL), NULL);
1483     if (!Draw_VirtualWindows)
1484     {
1485       SetWindowPos (aWin, HWND_TOPMOST,   1,1,1,1, SWP_NOMOVE);
1486       SetWindowPos (aWin, HWND_NOTOPMOST, 1,1,1,1, SWP_NOMOVE);
1487     }
1488     return aWin;
1489   }
1490   else
1491   {
1492     HANDLE hInstance = (HANDLE )GetWindowLongPtrW (hWndClient, GWLP_HINSTANCE);
1493     return CreateMDIWindowW (DRAWCLASS, DRAWTITLE,
1494                              WS_CAPTION | WS_CHILD | WS_THICKFRAME,
1495                              1,1,0,0,
1496                              hWndClient, (HINSTANCE)hInstance, nitem);
1497   }
1498 }
1499 
1500 /*--------------------------------------------------------*\
1501 |  DRAW WINDOW PROCEDURE
1502 \*--------------------------------------------------------*/
DrawProc(HWND hWnd,UINT wMsg,WPARAM wParam,LPARAM lParam)1503 LRESULT APIENTRY Draw_Window::DrawProc (HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam)
1504 {
1505   Draw_Window* aLocWin = (Draw_Window* )GetWindowLongPtrW (hWnd, CLIENTWND);
1506   if (aLocWin == NULL)
1507   {
1508     return Draw_IsConsoleSubsystem
1509          ? DefWindowProcW   (hWnd, wMsg, wParam, lParam)
1510          : DefMDIChildProcW (hWnd, wMsg, wParam, lParam);
1511   }
1512 
1513   switch (wMsg)
1514   {
1515     case WM_CLOSE:
1516     {
1517       aLocWin->Hide();
1518       return 0; // do nothing - window destruction should be performed by application
1519     }
1520     case WM_PAINT:
1521     {
1522       PAINTSTRUCT ps;
1523       BeginPaint (hWnd, &ps);
1524       if (aLocWin->GetUseBuffer())
1525       {
1526         aLocWin->Redraw();
1527       }
1528       else
1529       {
1530         aLocWin->WExpose();
1531       }
1532       EndPaint (hWnd, &ps);
1533       return 0;
1534     }
1535     case WM_SIZE:
1536     {
1537       if (aLocWin->GetUseBuffer())
1538       {
1539         aLocWin->InitBuffer();
1540         aLocWin->WExpose();
1541         aLocWin->Redraw();
1542         return 0;
1543       }
1544       break;
1545     }
1546   }
1547   return Draw_IsConsoleSubsystem
1548        ? DefWindowProcW   (hWnd, wMsg, wParam, lParam)
1549        : DefMDIChildProcW (hWnd, wMsg, wParam, lParam);
1550 }
1551 
1552 /*--------------------------------------------------------*\
1553 |  SelectWait
1554 \*--------------------------------------------------------*/
SelectWait(HANDLE & theWindow,int & theX,int & theY,int & theButton)1555 void Draw_Window::SelectWait (HANDLE& theWindow,
1556                               int& theX, int& theY,
1557                               int& theButton)
1558 {
1559   MSG aMsg;
1560   aMsg.wParam = 1;
1561   GetMessageW (&aMsg, NULL, 0, 0);
1562   while ((aMsg.message != WM_RBUTTONDOWN
1563        && aMsg.message != WM_LBUTTONDOWN)
1564        || !(Draw_IsConsoleSubsystem || IsChild(Draw_Window::hWndClientMDI, aMsg.hwnd)))
1565   {
1566     GetMessageW (&aMsg, NULL, 0, 0);
1567   }
1568 
1569   theWindow = aMsg.hwnd;
1570   theX = LOWORD(aMsg.lParam);
1571   theY = HIWORD(aMsg.lParam);
1572   if (aMsg.message == WM_LBUTTONDOWN)
1573   {
1574     theButton = 1;
1575   }
1576   else
1577   {
1578     theButton = 3;
1579   }
1580 }
1581 
1582 /*--------------------------------------------------------*\
1583 |  SelectNoWait
1584 \*--------------------------------------------------------*/
SelectNoWait(HANDLE & theWindow,int & theX,int & theY,int & theButton)1585 void Draw_Window::SelectNoWait (HANDLE& theWindow,
1586                                 int& theX, int& theY,
1587                                 int& theButton)
1588 {
1589   MSG aMsg;
1590   aMsg.wParam = 1;
1591   GetMessageW (&aMsg, NULL, 0, 0);
1592   while ((aMsg.message != WM_RBUTTONDOWN
1593        && aMsg.message != WM_LBUTTONDOWN
1594        && aMsg.message != WM_MOUSEMOVE)
1595        || !(Draw_IsConsoleSubsystem || IsChild(Draw_Window::hWndClientMDI, aMsg.hwnd)))
1596   {
1597     GetMessageW (&aMsg, NULL, 0, 0);
1598   }
1599 
1600   theWindow = aMsg.hwnd;
1601   theX = LOWORD(aMsg.lParam);
1602   theY = HIWORD(aMsg.lParam);
1603   switch (aMsg.message)
1604   {
1605     case WM_LBUTTONDOWN:
1606       theButton = 1;
1607       break;
1608     case WM_RBUTTONDOWN:
1609       theButton = 3;
1610       break;
1611     case WM_MOUSEMOVE:
1612       theButton = 0;
1613       break;
1614   }
1615 }
1616 
1617 /*--------------------------------------------------------*\
1618 |  Init
1619 \*--------------------------------------------------------*/
1620 
1621 static DWORD WINAPI tkLoop (LPVOID theThreadParameter);
1622 #ifdef _TK
1623 static Tk_Window mainWindow;
1624 #endif
1625 
1626 //* threads synchronization *//
1627 static DWORD dwMainThreadId;
1628 console_semaphore_value volatile console_semaphore = WAIT_CONSOLE_COMMAND;
1629 wchar_t console_command[DRAW_COMMAND_SIZE + 1];
1630 bool volatile isTkLoopStarted = false;
1631 
1632 /*--------------------------------------------------------*\
1633 |  Init_Appli
1634 \*--------------------------------------------------------*/
Init_Appli(HINSTANCE hInst,HINSTANCE hPrevInst,int nShow,HWND & hWndFrame)1635 Standard_Boolean Init_Appli(HINSTANCE hInst,
1636                             HINSTANCE hPrevInst, int nShow, HWND& hWndFrame )
1637 {
1638   DWORD IDThread;
1639   HANDLE hThread;
1640   console_semaphore = STOP_CONSOLE;
1641 
1642   dwMainThreadId = GetCurrentThreadId();
1643 
1644   //necessary for normal Tk operation
1645   hThread = CreateThread (NULL,       // no security attributes
1646                           0,          // use default stack size
1647                           tkLoop,     // thread function
1648                           NULL,       // no thread function argument
1649                           0,          // use default creation flags
1650                           &IDThread);
1651   if (!hThread) {
1652     std::cout << "Failed to create Tcl/Tk main loop thread. Switching to batch mode..." << std::endl;
1653     Draw_Batch = Standard_True;
1654     Draw_Interpretor& aCommands = Draw::GetInterpretor();
1655     aCommands.Init();
1656     Tcl_Interp *interp = aCommands.Interp();
1657     Tcl_Init(interp);
1658 #ifdef _TK
1659     try {
1660       OCC_CATCH_SIGNALS
1661       Tk_Init(interp);
1662     } catch  (Standard_Failure& anExcept) {
1663       std::cout << "Failed to initialize Tk: " << anExcept.GetMessageString() << std::endl;
1664     }
1665 
1666     Tcl_StaticPackage(interp, "Tk", Tk_Init, (Tcl_PackageInitProc *) NULL);
1667 #endif
1668     //since the main Tcl/Tk loop wasn't created --> switch to batch mode
1669     return Standard_False;
1670   }
1671 
1672   // san - 06/08/2002 - Time for tkLoop to start; Tk fails to initialize otherwise
1673   while (!isTkLoopStarted)
1674   {
1675     Sleep (10);
1676   }
1677 
1678   // Saving of window classes
1679   if (!hPrevInst)
1680   {
1681     if (!RegisterAppClass (hInst))
1682     {
1683       return Standard_False;
1684     }
1685   }
1686 
1687   /*
1688    ** Enter the application message-polling loop.  This is the anchor for
1689    ** the application.
1690   */
1691   hWndFrame = !Draw_IsConsoleSubsystem ? CreateAppWindow (hInst) : NULL;
1692   if (hWndFrame != NULL)
1693   {
1694     ShowWindow (hWndFrame, nShow);
1695     UpdateWindow (hWndFrame);
1696   }
1697 
1698   return Standard_True;
1699 }
1700 
1701 Standard_Boolean Draw_Interprete (const char*);
1702 
1703 /*--------------------------------------------------------*\
1704 |  readStdinThreadFunc
1705 \*--------------------------------------------------------*/
readStdinThreadFunc(const LPVOID theThreadParameter)1706 static DWORD WINAPI readStdinThreadFunc (const LPVOID theThreadParameter)
1707 {
1708   (void)theThreadParameter;
1709   if (!Draw_IsConsoleSubsystem)
1710   {
1711     return 1;
1712   }
1713 
1714   // Console locale could be set to the system codepage .OCP (UTF-8 is not properly supported on Windows).
1715   // However, to use it, we have to care using std::wcerr/fwprintf/WriteConsoleW for non-ascii strings everywhere (including Tcl itself),
1716   // or otherwise we can have incomplete output issues
1717   // (e.g. UNICODE string will be NOT just corrupted as in case when we don't set setlocale()
1718   // but will break further normal output to console due to special characters being accidentally handled by console in the wrong way).
1719   //setlocale (LC_ALL, ".OCP");
1720 
1721   // _O_U16TEXT can be used with fgetws() to get similar result as ReadConsoleW() without affecting setlocale(),
1722   // however it would break pipe input
1723   //_setmode (_fileno(stdin), _O_U16TEXT);
1724 
1725   bool isConsoleInput = true;
1726   for (;;)
1727   {
1728     while (console_semaphore != WAIT_CONSOLE_COMMAND)
1729     {
1730       Sleep (100);
1731     }
1732 
1733     const HANDLE anStdIn = ::GetStdHandle (STD_INPUT_HANDLE);
1734     if (anStdIn != NULL
1735      && anStdIn != INVALID_HANDLE_VALUE
1736      && isConsoleInput)
1737     {
1738       DWORD aNbRead = 0;
1739       if (ReadConsoleW (anStdIn, console_command, DRAW_COMMAND_SIZE, &aNbRead, NULL))
1740       {
1741         console_command[aNbRead] = L'\0';
1742         console_semaphore = HAS_CONSOLE_COMMAND;
1743         continue;
1744       }
1745       else
1746       {
1747         const DWORD anErr = GetLastError();
1748         if (anErr != ERROR_SUCCESS)
1749         {
1750           // fallback using fgetws() which would work with pipes
1751           // but supports Unicode only through multi-byte encoding (which is not UTF-8)
1752           isConsoleInput = false;
1753           continue;
1754         }
1755       }
1756     }
1757 
1758     // fgetws() works only for characters within active locale (see setlocale())
1759     if (fgetws (console_command, DRAW_COMMAND_SIZE, stdin))
1760     {
1761       console_semaphore = HAS_CONSOLE_COMMAND;
1762     }
1763   }
1764 }
1765 
1766 /*--------------------------------------------------------*\
1767 |  exitProc: finalization handler for Tcl/Tk thread. Forces parent process to die
1768 \*--------------------------------------------------------*/
exitProc(ClientData)1769 void exitProc(ClientData /*dc*/)
1770 {
1771   for (NCollection_List<Draw_Window::FCallbackBeforeTerminate>::Iterator anIter (TermCallbacks());
1772        anIter.More(); anIter.Next())
1773   {
1774     (*anIter.Value())();
1775   }
1776   HANDLE proc = GetCurrentProcess();
1777   TerminateProcess(proc, 0);
1778 }
1779 
1780 // This is fixed version of TclpGetDefaultStdChannel() defined in tclWinChan.c
1781 // See https://core.tcl.tk/tcl/tktview/91c9bc1c457fda269ae18595944fc3c2b54d961d
TclpGetDefaultStdChannel(int type)1782 static Tcl_Channel TclpGetDefaultStdChannel (int type) // One of TCL_STDIN, TCL_STDOUT, or TCL_STDERR.
1783 {
1784     Tcl_Channel channel;
1785     HANDLE handle;
1786     int mode = -1;
1787     const char *bufMode = NULL;
1788     DWORD handleId = (DWORD) -1;
1789 				/* Standard handle to retrieve. */
1790 
1791     switch (type) {
1792     case TCL_STDIN:
1793 	handleId = STD_INPUT_HANDLE;
1794 	mode = TCL_READABLE;
1795 	bufMode = "line";
1796 	break;
1797     case TCL_STDOUT:
1798 	handleId = STD_OUTPUT_HANDLE;
1799 	mode = TCL_WRITABLE;
1800 	bufMode = "line";
1801 	break;
1802     case TCL_STDERR:
1803 	handleId = STD_ERROR_HANDLE;
1804 	mode = TCL_WRITABLE;
1805 	bufMode = "none";
1806 	break;
1807     default:
1808 	Tcl_Panic("TclGetDefaultStdChannel: Unexpected channel type");
1809 	break;
1810     }
1811 
1812     handle = GetStdHandle(handleId);
1813 
1814     /*
1815      * Note that we need to check for 0 because Windows may return 0 if this
1816      * is not a console mode application, even though this is not a valid
1817      * handle.
1818      */
1819 
1820     if ((handle == INVALID_HANDLE_VALUE) || (handle == 0)) {
1821 	return (Tcl_Channel) NULL;
1822     }
1823 
1824     /*
1825      * Make duplicate of the standard handle as it may be altered
1826      * (closed, reopened with another type of the object etc.) by
1827      * the system or a user code at any time, e.g. by call to _dup2()
1828      */
1829     if (! DuplicateHandle (GetCurrentProcess(), handle,
1830                            GetCurrentProcess(), &handle,
1831                            0, FALSE, DUPLICATE_SAME_ACCESS)) {
1832 	return (Tcl_Channel) NULL;
1833     }
1834 
1835     channel = Tcl_MakeFileChannel(handle, mode);
1836 
1837     if (channel == NULL) {
1838 	return (Tcl_Channel) NULL;
1839     }
1840 
1841     /*
1842      * Set up the normal channel options for stdio handles.
1843      */
1844 
1845     if (Tcl_SetChannelOption(NULL,channel,"-translation","auto")!=TCL_OK ||
1846 	    Tcl_SetChannelOption(NULL,channel,"-eofchar","\032 {}")!=TCL_OK ||
1847 	    Tcl_SetChannelOption(NULL,channel,"-buffering",bufMode)!=TCL_OK) {
1848 	Tcl_Close(NULL, channel);
1849 	return (Tcl_Channel) NULL;
1850     }
1851     return channel;
1852 }
1853 
1854 // helper function
ResetStdChannel(int type)1855 static void ResetStdChannel (int type)
1856 {
1857   Tcl_Channel aChannel = TclpGetDefaultStdChannel (type);
1858   Tcl_SetStdChannel (aChannel, type);
1859   if (aChannel)
1860   {
1861     Tcl_RegisterChannel (NULL, aChannel);
1862   }
1863 }
1864 
1865 /*--------------------------------------------------------*\
1866 |  tkLoop: implements Tk_Main()-like behaviour in a separate thread
1867 \*--------------------------------------------------------*/
tkLoop(const LPVOID theThreadParameter)1868 static DWORD WINAPI tkLoop (const LPVOID theThreadParameter)
1869 {
1870   (void)theThreadParameter;
1871   Tcl_CreateExitHandler(exitProc, 0);
1872 
1873   Draw_Interpretor& aCommands = Draw::GetInterpretor();
1874   aCommands.Init();
1875   Tcl_Interp* interp = aCommands.Interp();
1876   Tcl_Init (interp);
1877 
1878   // Work-around against issue with Tcl standard channels on Windows.
1879   // These channels by default use OS handles owned by the system which
1880   // may get invalidated e.g. by dup2() (see dlog command).
1881   // If this happens, output to stdout from Tcl (e.g. puts) gets broken
1882   // (sympthom is error message: "error writing "stdout": bad file number").
1883   // To prevent this, we set standard channels using duplicate of system handles.
1884   // The effect is that Tcl channel becomes independent on C file descriptor
1885   // and even if stdout/stderr are redirected using dup2(), Tcl keeps using
1886   // original device.
1887   ResetStdChannel (TCL_STDOUT);
1888   ResetStdChannel (TCL_STDERR);
1889 
1890 #if (TCL_MAJOR_VERSION > 8) || ((TCL_MAJOR_VERSION == 8) && (TCL_MINOR_VERSION >= 5))
1891   // Plain Tcl (8.6.4+) initializes interpretor channels automatically, but
1892   // ActiveState Tcl (at least 8.6.4) does not seem to do that, so channels
1893   // need to be set into interpretor explicitly
1894   {
1895     Tcl_Channel aChannelIn  = Tcl_GetStdChannel (TCL_STDIN);
1896     Tcl_Channel aChannelOut = Tcl_GetStdChannel (TCL_STDOUT);
1897     Tcl_Channel aChannelErr = Tcl_GetStdChannel (TCL_STDERR);
1898     if (aChannelIn != NULL)
1899     {
1900       Tcl_RegisterChannel (aCommands.Interp(), aChannelIn);
1901     }
1902     if (aChannelOut != NULL)
1903     {
1904       Tcl_RegisterChannel (aCommands.Interp(), aChannelOut);
1905     }
1906     if (aChannelErr != NULL)
1907     {
1908       Tcl_RegisterChannel (aCommands.Interp(), aChannelErr);
1909     }
1910   }
1911 #endif
1912 
1913 #ifdef _TK
1914   // initialize the Tk library if not in 'virtual windows' mode
1915   // (virtual windows are created by OCCT with native APIs,
1916   // thus Tk will be useless)
1917   if (!Draw_VirtualWindows)
1918   {
1919     try
1920     {
1921       OCC_CATCH_SIGNALS
1922       Standard_Integer res = Tk_Init (interp);
1923       if (res != TCL_OK)
1924       {
1925 #if ((TCL_MAJOR_VERSION > 8) || ((TCL_MAJOR_VERSION == 8) && (TCL_MINOR_VERSION >= 5)))
1926         std::cout << "tkLoop: error in Tk initialization. Tcl reported: " << Tcl_GetStringResult(interp) << std::endl;
1927 #else
1928         std::cout << "tkLoop: error in Tk initialization. Tcl reported: " << interp->result << std::endl;
1929 #endif
1930       }
1931     }
1932     catch (const Standard_Failure&)
1933     {
1934       std::cout << "tkLoop: exception in TK_Init\n";
1935     }
1936     Tcl_StaticPackage (interp, "Tk", Tk_Init, (Tcl_PackageInitProc* ) NULL);
1937     mainWindow = Tk_MainWindow (interp);
1938     if (mainWindow == NULL)
1939     {
1940 #if ((TCL_MAJOR_VERSION > 8) || ((TCL_MAJOR_VERSION == 8) && (TCL_MINOR_VERSION >= 5)))
1941       fprintf (stderr, "%s\n", Tcl_GetStringResult(interp));
1942 #else
1943       fprintf (stderr, "%s\n", interp->result);
1944 #endif
1945       std::cout << "tkLoop: Tk_MainWindow() returned NULL. Exiting...\n";
1946       Tcl_Exit (0);
1947     }
1948     Tk_Name(mainWindow) = Tk_GetUid (Tk_SetAppName (mainWindow, "Draw"));
1949   }
1950 #endif //#ifdef _TK
1951 
1952   // set signal handler in the new thread
1953   OSD::SetSignal(Standard_False);
1954 
1955   // inform the others that we have started
1956   isTkLoopStarted = true;
1957 
1958   while (console_semaphore == STOP_CONSOLE)
1959   {
1960     Tcl_DoOneEvent (TCL_ALL_EVENTS | TCL_DONT_WAIT);
1961   }
1962 
1963   if (Draw_IsConsoleSubsystem
1964    && console_semaphore == WAIT_CONSOLE_COMMAND)
1965   {
1966     Prompt (interp, 0);
1967   }
1968 
1969   //process a command
1970   Standard_Boolean toLoop = Standard_True;
1971   while (toLoop)
1972   {
1973     // The natural way is first flushing events, already put into queue, and then processing custom code in-between.
1974     // Unfortunately, Tcl has no API returning the number of queued events like XPending(), and only empty state can be checked.
1975     // Since events can be continuously fed from parallel threads, Tcl_DoOneEvent might never return empty state at all.
1976     const bool isTclEventQueueEmpty = Tcl_DoOneEvent(TCL_ALL_EVENTS | TCL_DONT_WAIT) == 0;
1977     if (console_semaphore == HAS_CONSOLE_COMMAND)
1978     {
1979       const TCollection_AsciiString aCmdUtf8 (console_command);
1980       const bool wasInterpreted = Draw_Interprete (aCmdUtf8.ToCString());
1981       if (Draw_IsConsoleSubsystem)
1982       {
1983         Prompt (interp, wasInterpreted ? 0 : 1);
1984       }
1985       console_semaphore = WAIT_CONSOLE_COMMAND;
1986     }
1987     else if (isTclEventQueueEmpty)
1988     {
1989       // release CPU while polling
1990       Sleep (1);
1991     }
1992   #ifdef _TK
1993     // We should not exit until the Main Tk window is closed
1994     toLoop = (Draw_VirtualWindows || Tk_GetNumMainWindows() > 0);
1995   #endif
1996   }
1997   Tcl_Exit(0);
1998   return 0;
1999 }
2000 
2001 /*--------------------------------------------------------*\
2002 |  Run_Appli
2003 \*--------------------------------------------------------*/
Run_Appli(HWND hWnd)2004 void Run_Appli (HWND hWnd)
2005 {
2006   MSG msg;
2007   HACCEL hAccel = NULL;
2008   msg.wParam = 1;
2009 
2010 //  if (!(hAccel = LoadAccelerators (hInstance, MAKEINTRESOURCE(ACCEL_ID))))
2011 //        MessageBox(hWnd, "MDI: Load Accel failure!", "Error", MB_OK);
2012   DWORD IDThread;
2013   HANDLE hThread;
2014   if (Draw_IsConsoleSubsystem)
2015   {
2016     hThread = CreateThread (NULL,                // no security attributes
2017                             0,                   // use default stack size
2018                             readStdinThreadFunc, // thread function
2019                             NULL,                // no thread function argument
2020                             0,                   // use default creation flags
2021                             &IDThread);          // returns thread identifier
2022     if (!hThread)
2023     {
2024       std::cout << "pb in creation of the thread reading stdin" << std::endl;
2025       Draw_IsConsoleSubsystem = Standard_False;
2026       Init_Appli (GetModuleHandleW (NULL),
2027                   GetModuleHandleW (NULL),
2028                   1, hWnd); // reinit => create MDI client wnd
2029     }
2030   }
2031 
2032   //turn on the command interpretation mechanism (regardless of the mode)
2033   if (console_semaphore == STOP_CONSOLE)
2034   {
2035     console_semaphore = WAIT_CONSOLE_COMMAND;
2036   }
2037 
2038   //simple Win32 message loop
2039   while (GetMessageW (&msg, NULL, 0, 0) > 0)
2040   {
2041     if (!TranslateAcceleratorW (hWnd, hAccel, &msg))
2042     {
2043       TranslateMessage (&msg);
2044       DispatchMessageW (&msg);
2045     }
2046   }
2047   ExitProcess(0);
2048 }
2049 
2050 /*--------------------------------------------------------*\
2051 |  Destroy_Appli
2052 \*--------------------------------------------------------*/
Destroy_Appli(HINSTANCE hInst)2053 void Destroy_Appli (HINSTANCE hInst)
2054 {
2055   UnregisterAppClass (hInst);
2056   for (int i = 0; i < MAXCOLOR; ++i)
2057   {
2058     DeleteObject (Draw_colorPenTab[i]);
2059   }
2060 }
2061 
2062 #endif
2063