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