Merge branch '138-toggle-free-look-with-hotkey' into 'main/atys-live'
[ryzomcore.git] / nel / src / misc / win_displayer.cpp
blob50bef6030f36fa86e7737dd3b8a2d371b8645468
1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2014-2020 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
6 //
7 // This program is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU Affero General Public License as
9 // published by the Free Software Foundation, either version 3 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU Affero General Public License for more details.
17 // You should have received a copy of the GNU Affero General Public License
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #include "stdmisc.h"
21 #include "nel/misc/win_displayer.h"
23 #ifdef NL_OS_WINDOWS
24 #include <windowsx.h>
25 #include <winuser.h>
26 #include <cstring>
27 #include <cstdlib>
28 #include <richedit.h>
29 #include <commctrl.h>
30 #include <ctime>
32 #include "nel/misc/app_context.h"
33 #include "nel/misc/path.h"
34 #include "nel/misc/command.h"
35 #include "nel/misc/thread.h"
36 #include "nel/misc/ucstring.h"
38 using namespace std;
40 #ifdef DEBUG_NEW
41 #define new DEBUG_NEW
42 #endif
44 namespace NLMISC {
46 static CHARFORMAT2A CharFormat;
48 CWinDisplayer::CWinDisplayer(const char *displayerName) : CWindowDisplayer(displayerName), Exit(false)
50 needSlashR = true;
51 createLabel("@Clear|CLEAR");
53 INelContext::getInstance().setWindowedApplication(true);
56 CWinDisplayer::~CWinDisplayer ()
60 LRESULT CALLBACK WndProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
62 MSGFILTER *pmf;
64 switch (message)
66 case WM_ACTIVATE:
68 if (LOWORD(wParam) != WA_INACTIVE)
70 CWinDisplayer *cwd=(CWinDisplayer *)GetWindowLongPtrW (hWnd, GWLP_USERDATA);
71 if (cwd != NULL)
72 SetFocus(cwd->_HInputEdit);
73 return 0;
76 break;
77 case WM_SIZE:
79 CWinDisplayer *cwd=(CWinDisplayer *)GetWindowLongPtrW (hWnd, GWLP_USERDATA);
80 if (cwd != NULL)
82 int w = lParam & 0xFFFF;
83 int h = (lParam >> 16) & 0xFFFF;
85 // SetWindowPos (cwd->_HEdit, NULL, 0, cwd->_ToolBarHeight, w, h-cwd->_ToolBarHeight-cwd->_InputEditHeight, SWP_NOZORDER | SWP_NOACTIVATE );
86 SetWindowPos (cwd->_HInputEdit, NULL, 0, h-cwd->_InputEditHeight, w, cwd->_InputEditHeight, SWP_NOZORDER | SWP_NOACTIVATE );
87 cwd->resizeLabels ();
90 break;
91 case WM_DESTROY: PostQuitMessage (0); break;
92 case WM_CLOSE:
94 CWinDisplayer *cwd=(CWinDisplayer *)GetWindowLongPtrW (hWnd, GWLP_USERDATA);
95 if (cwd != NULL)
96 cwd->_Continue = false;
98 break;
99 case WM_COMMAND:
101 if (HIWORD(wParam) == BN_CLICKED)
103 CWinDisplayer *cwd=(CWinDisplayer *)GetWindowLongPtrW (hWnd, GWLP_USERDATA);
104 // find the button and execute the command
105 CSynchronized<std::vector<CWindowDisplayer::CLabelEntry> >::CAccessor access (&cwd->_Labels);
106 for (uint i = 0; i < access.value().size(); i++)
108 if (access.value()[i].Hwnd == (HWND)lParam)
110 if(access.value()[i].Value == "@Clear|CLEAR")
112 // special commands because the clear must be called by the display thread and not main thread
113 cwd->clear ();
115 else
117 // the button was found, add the command in the command stack
118 CSynchronized<std::vector<std::string> >::CAccessor accessCommands (&cwd->_CommandsToExecute);
119 string str;
120 nlassert (!access.value()[i].Value.empty());
121 nlassert (access.value()[i].Value[0] == '@');
123 string::size_type pos = access.value()[i].Value.find ("|");
124 if (pos != string::npos)
126 str = access.value()[i].Value.substr(pos+1);
128 else
130 str = access.value()[i].Value.substr(1);
132 if (!str.empty())
133 accessCommands.value().push_back(str);
135 break;
140 break;
141 case WM_NOTIFY:
142 switch (((NMHDR*)lParam)->code)
144 case EN_MSGFILTER:
145 pmf = (MSGFILTER *)lParam;
146 if (pmf->msg == WM_CHAR)
148 if (pmf->wParam == VK_RETURN)
150 WCHAR wText[20000];
151 string TextSend;
152 CWinDisplayer *cwd=(CWinDisplayer *)GetWindowLongPtr (hWnd, GWLP_USERDATA);
153 // get the text as unicode string
154 GetWindowTextW(cwd->_HInputEdit, wText, 20000);
155 // and convert it to UTF-8 encoding.
156 TextSend = wideToUtf8(wText);
157 SendMessageA (cwd->_HInputEdit, WM_SETTEXT, (WPARAM)0, (LPARAM)"");
158 const char *pos2 = TextSend.c_str();
159 string str;
160 while (*pos2 != '\0')
162 str.clear();
164 // get the string
165 while (*pos2 != '\0' && *pos2 != '\n')
167 if (*pos2 != '\r')
169 str += *pos2;
171 pos2++;
174 // eat the \n
175 if (*pos2 == '\n')
176 pos2++;
178 if (!str.empty())
181 CSynchronized<std::vector<std::string> >::CAccessor access (&cwd->_CommandsToExecute);
182 access.value().push_back(str);
184 cwd->_History.push_back(str);
185 cwd->_PosInHistory = (uint)cwd->_History.size();
189 else if (pmf->wParam == VK_TAB)
191 WCHAR wText[20000];
194 CWinDisplayer *cwd=(CWinDisplayer *)GetWindowLongPtrW (hWnd, GWLP_USERDATA);
196 // get the text as unicode string
197 GetWindowTextW(cwd->_HInputEdit, wText, 20000);
198 // and convert it to UTF-8 encoding
199 string str = wideToUtf8(wText);
200 nlassert (cwd->Log != NULL);
201 ICommand::expand (str, *cwd->Log);
202 SendMessageW (cwd->_HInputEdit, WM_SETTEXT, (WPARAM)0, (LPARAM)wText);
204 SendMessageA (cwd->_HInputEdit, EM_SETSEL, wcslen(wText), wcslen(wText));
206 return 1;
209 else if (pmf->msg == WM_KEYUP)
211 if (pmf->wParam == VK_UP)
213 CWinDisplayer *cwd=(CWinDisplayer *)GetWindowLongPtrA (hWnd, GWLP_USERDATA);
215 if (cwd->_PosInHistory > 0)
216 cwd->_PosInHistory--;
218 if (!cwd->_History.empty())
220 ucstring ucs;
221 // convert the text from UTF-8 to unicode
222 ucs.fromUtf8(cwd->_History[cwd->_PosInHistory]);
224 // set the text as unicode string
225 if (!SetWindowTextW(cwd->_HInputEdit, (LPCWSTR)ucs.c_str()))
227 nlwarning("SetWindowText failed: %s", formatErrorMessage(getLastError()).c_str());
230 SendMessageA (cwd->_HInputEdit, EM_SETSEL, (WPARAM)ucs.size(), (LPARAM)ucs.size());
233 else if (pmf->wParam == VK_DOWN)
235 CWinDisplayer *cwd=(CWinDisplayer *)GetWindowLongPtrW (hWnd, GWLP_USERDATA);
237 if (cwd->_PosInHistory < cwd->_History.size()-1)
238 cwd->_PosInHistory++;
240 if (!cwd->_History.empty() && cwd->_PosInHistory < cwd->_History.size())
242 ucstring ucs;
243 // convert the text from UTF-8 to unicode
244 ucs.fromUtf8(cwd->_History[cwd->_PosInHistory]);
246 // set the text as unicode string
247 if (!SetWindowTextW(cwd->_HInputEdit, (LPCWSTR)ucs.c_str()))
249 nlwarning("SetWindowText failed: %s", formatErrorMessage(getLastError()).c_str());
252 SendMessageA (cwd->_HInputEdit, EM_SETSEL, (WPARAM)ucs.size(), (LPARAM)ucs.size());
259 return DefWindowProcW (hWnd, message, wParam, lParam);
262 void CWinDisplayer::updateLabels ()
264 bool needResize = false;
266 CSynchronized<std::vector<CLabelEntry> >::CAccessor access (&_Labels);
267 for (uint i = 0; i < access.value().size(); i++)
269 if (access.value()[i].NeedUpdate && !access.value()[i].Value.empty())
271 if (access.value()[i].Hwnd == NULL)
273 // create a button for command and label for variables
274 if (access.value()[i].Value[0] == '@')
276 access.value()[i].Hwnd = CreateWindowA ("BUTTON", "", WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON, 0, 0, 0, 0, _HWnd, (HMENU) NULL, (HINSTANCE) GetWindowLongPtrA(_HWnd, GWLP_HINSTANCE), NULL);
278 else
280 access.value()[i].Hwnd = CreateWindowA ("STATIC", "", WS_CHILD | WS_VISIBLE | SS_SIMPLE, 0, 0, 0, 0, _HWnd, (HMENU) NULL, (HINSTANCE) GetWindowLongPtrA(_HWnd, GWLP_HINSTANCE), NULL);
282 SendMessageA ((HWND)access.value()[i].Hwnd, WM_SETFONT, (WPARAM)_HFont, TRUE);
283 needResize = true;
286 string n;
288 // do this tricks to be sure that windows will clear what is after the number
289 if (access.value()[i].Value[0] != '@')
290 n = access.value()[i].Value + " ";
291 else
293 string::size_type pos = access.value()[i].Value.find ('|');
294 if (pos != string::npos)
296 n = access.value()[i].Value.substr (1, pos - 1);
298 else
300 n = access.value()[i].Value.substr (1);
304 SendMessageW((HWND)access.value()[i].Hwnd, WM_SETTEXT, 0, (LPARAM)nlUtf8ToWide(n));
305 access.value()[i].NeedUpdate = false;
309 if (needResize)
310 resizeLabels();
313 void CWinDisplayer::resizeLabels ()
316 CSynchronized<std::vector<CLabelEntry> >::CAccessor access (&_Labels);
318 RECT Rect;
319 GetClientRect (_HWnd, &Rect);
321 uint i = 0, nb;
322 uint y = 0, nby = 1;
324 for (i = 0; i < access.value().size (); i++)
325 if (access.value()[i].Value.empty())
326 nby++;
328 i = 0;
330 for(;;)
332 nb = 0;
333 while (i+nb != access.value().size () && !access.value()[i+nb].Value.empty()) nb++;
335 sint delta;
336 if (nb == 0)
337 delta = 0;
338 else
339 delta = Rect.right / nb;
341 for (uint j = 0; j< nb; j++)
343 if ((HWND)access.value()[i+j].Hwnd != NULL)
344 SetWindowPos ((HWND)access.value()[i+j].Hwnd, NULL, j*delta, y*_ToolBarHeight, delta, _ToolBarHeight, SWP_NOZORDER | SWP_NOACTIVATE );
346 i += nb + 1;
347 y++;
348 if (i >= access.value().size())
349 break;
352 SetWindowPos (_HEdit, NULL, 0, nby*_ToolBarHeight, Rect.right, Rect.bottom-nby*_ToolBarHeight-_InputEditHeight, SWP_NOZORDER | SWP_NOACTIVATE );
356 void CWinDisplayer::setTitleBar (const string &titleBar)
358 string wn;
359 if (!titleBar.empty())
361 wn += titleBar;
362 wn += ": ";
364 wn += "Nel Service Console (compiled " __DATE__ " " __TIME__ " in " + nlMode + " mode)";
366 nldebug("SERVICE: Set title bar to '%s'", wn.c_str());
368 if (!SetWindowTextW(_HWnd, (LPWSTR)ucstring::makeFromUtf8(wn).c_str()))
370 nlwarning("SetWindowText failed: %s", formatErrorMessage(getLastError()).c_str());
374 void CWinDisplayer::open (string titleBar, bool iconified, sint x, sint y, sint w, sint h, sint hs, sint fs, const std::string &fn, bool ww, CLog *log)
376 if (w == -1)
377 w = 700;
378 if (h == -1)
379 h = 300;
380 if (hs == -1)
381 hs = 1000;
383 Log = log;
385 _HistorySize = hs;
387 WNDCLASSW wc;
388 memset (&wc,0,sizeof(wc));
389 wc.style = CS_HREDRAW | CS_VREDRAW ;//| CS_DBLCLKS;
390 wc.lpfnWndProc = (WNDPROC)WndProc;
391 wc.cbClsExtra = 0;
392 wc.cbWndExtra = 0;
393 wc.hInstance = GetModuleHandleW(NULL);
394 wc.hIcon = NULL;
395 wc.hCursor = LoadCursorW(NULL,(LPWSTR)IDC_ARROW);
396 wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
397 wc.lpszClassName = L"NLDisplayerClass";
398 wc.lpszMenuName = NULL;
399 if ( !RegisterClassW(&wc) ) return;
401 ULONG WndFlags;
402 RECT WndRect;
403 WndFlags = WS_OVERLAPPEDWINDOW /*| WS_CLIPCHILDREN | WS_CLIPSIBLINGS*/;
404 WndRect.left = 0;
405 WndRect.top = 0;
406 WndRect.right = w;
407 WndRect.bottom = h;
408 AdjustWindowRect(&WndRect,WndFlags,FALSE);
410 // create the window
411 _HWnd = CreateWindowW (L"NLDisplayerClass", L"", WndFlags, CW_USEDEFAULT,CW_USEDEFAULT, WndRect.right,WndRect.bottom, NULL, NULL, GetModuleHandle(NULL), NULL);
412 SetWindowLongPtrW (_HWnd, GWLP_USERDATA, (LONG_PTR)this);
414 _HLibModule = LoadLibraryW(L"RICHED20.DLL");
415 if (_HLibModule == NULL)
417 nlerror ("RichEdit 2.0 library not found!");
420 string rfn;
421 if (fn.empty())
422 rfn = "courier";
423 else
424 rfn = fn;
426 sint rfs;
427 if (fs == 0)
428 rfs = 10;
429 else
430 rfs = fs;
432 _HFont = CreateFontW (-MulDiv(rfs, GetDeviceCaps(GetDC(0),LOGPIXELSY), 72), 0, 0, 0, FW_DONTCARE, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, (LPCWSTR)ucstring::makeFromUtf8(rfn).c_str());
435 // create the edit control
436 DWORD dwStyle = WS_HSCROLL | WS_CHILD | WS_VISIBLE | WS_VSCROLL | ES_READONLY | ES_LEFT | ES_MULTILINE /*| ES_AUTOVSCROLL*/;
438 if(ww)
439 dwStyle &= ~WS_HSCROLL;
440 else
441 dwStyle |= WS_HSCROLL;
443 _HEdit = CreateWindowExW(WS_EX_OVERLAPPEDWINDOW, RICHEDIT_CLASSW, L"", dwStyle, 0, _ToolBarHeight, w, h-_ToolBarHeight-_InputEditHeight, _HWnd, (HMENU) NULL, (HINSTANCE) GetWindowLongPtr(_HWnd, GWLP_HINSTANCE), NULL);
444 SendMessageA (_HEdit, WM_SETFONT, (WPARAM)_HFont, TRUE);
446 // set the edit text limit to lot of :)
447 SendMessageA (_HEdit, EM_LIMITTEXT, -1, 0);
449 CharFormat.cbSize = sizeof(CharFormat);
450 CharFormat.dwMask = CFM_COLOR;
451 SendMessageA(_HEdit,EM_GETCHARFORMAT,(WPARAM)0,(LPARAM)&CharFormat);
452 CharFormat.dwEffects &= ~CFE_AUTOCOLOR;
454 // create the input edit control
455 _HInputEdit = CreateWindowExW(WS_EX_OVERLAPPEDWINDOW, RICHEDIT_CLASSW, L"", WS_CHILD | WS_VISIBLE
456 /*| ES_MULTILINE*/ | ES_WANTRETURN | ES_NOHIDESEL | ES_AUTOHSCROLL, 0, h-_InputEditHeight, w, _InputEditHeight,
457 _HWnd, NULL, (HINSTANCE) GetWindowLongPtr(_HWnd, GWLP_HINSTANCE), NULL);
458 SendMessageW (_HInputEdit, WM_SETFONT, (WPARAM)_HFont, TRUE);
460 LRESULT dwEvent = SendMessageW(_HInputEdit, EM_GETEVENTMASK, (WPARAM)0, (LPARAM)0);
461 dwEvent |= ENM_MOUSEEVENTS | ENM_KEYEVENTS | ENM_CHANGE;
462 SendMessageA(_HInputEdit, EM_SETEVENTMASK, (WPARAM)0, (LPARAM)dwEvent);
464 // resize the window
465 RECT rc;
466 SetRect (&rc, 0, 0, w, h);
467 AdjustWindowRectEx (&rc, GetWindowStyle (_HWnd), GetMenu (_HWnd) != NULL, GetWindowExStyle (_HWnd));
469 LONG flag = SWP_NOZORDER | SWP_NOACTIVATE;
471 if (x == -1 && y == -1) flag |= SWP_NOMOVE;
472 if (w == -1 && h == -1) flag |= SWP_NOSIZE;
474 SetWindowPos (_HWnd, NULL, x, y, w, h, flag);
475 SetWindowPos (_HWnd, NULL, x, y, rc.right - rc.left, rc.bottom - rc.top, flag);
477 setTitleBar (titleBar);
479 if (iconified)
480 ShowWindow(_HWnd,SW_MINIMIZE);
481 else
482 ShowWindow(_HWnd,SW_SHOW);
484 SetFocus (_HInputEdit);
486 _Init = true;
489 void CWinDisplayer::clear ()
491 bool focus = (GetFocus() == _HEdit);
492 if (focus)
494 SendMessageA(_HEdit,EM_SETOPTIONS,ECOOP_AND,(LPARAM)~ECO_AUTOVSCROLL);
495 SendMessageA(_HEdit,EM_SETOPTIONS,ECOOP_AND,(LPARAM)~ECO_AUTOHSCROLL);
498 // get number of line
499 LRESULT nLine = SendMessageW (_HEdit, EM_GETLINECOUNT, 0, 0) - 1;
501 // get size of the last line
502 LRESULT nIndex = SendMessageW (_HEdit, EM_LINEINDEX, nLine, 0);
504 // select all the text
505 SendMessageW (_HEdit, EM_SETSEL, 0, nIndex);
507 // clear all the text
508 SendMessageW (_HEdit, EM_REPLACESEL, FALSE, (LPARAM) L"");
510 SendMessageW(_HEdit,EM_SETMODIFY,(WPARAM)TRUE,(LPARAM)0);
512 if ( focus )
514 SendMessageW(_HEdit,EM_SETOPTIONS,ECOOP_OR,(LPARAM)ECO_AUTOVSCROLL);
515 SendMessageW(_HEdit,EM_SETOPTIONS,ECOOP_OR,(LPARAM)ECO_AUTOHSCROLL);
519 void CWinDisplayer::display_main ()
521 nlassert (_Init);
523 while (_Continue)
526 // Display the bufferized string
530 CSynchronized<std::list<std::pair<uint32, std::string> > >::CAccessor access (&_Buffer);
531 std::list<std::pair<uint32, std::string> >::iterator it;
533 sint vecSize = (sint)access.value().size();
534 //nlassert (vecSize <= _HistorySize);
536 if (vecSize > 0)
538 // look if we are at the bottom of the edit
539 SCROLLINFO info;
540 info.cbSize = sizeof(info);
541 info.fMask = SIF_ALL;
543 bool bottom = true;
544 if (GetScrollInfo(_HEdit,SB_VERT,&info) != 0)
545 bottom = (info.nPage == 0) || (info.nMax<=(info.nPos+(int)info.nPage));
547 // look if we have the focus
548 bool focus = (GetFocus() == _HEdit);
549 if (focus)
551 SendMessageA(_HEdit,EM_SETOPTIONS,ECOOP_AND,(LPARAM)~ECO_AUTOVSCROLL);
552 SendMessageA(_HEdit,EM_SETOPTIONS,ECOOP_AND,(LPARAM)~ECO_AUTOHSCROLL);
555 // store old selection
556 DWORD startSel, endSel;
557 SendMessageA (_HEdit, EM_GETSEL, (WPARAM)&startSel, (LPARAM)&endSel);
559 // find how many lines we have to remove in the current output to add new lines
561 // get number of line
562 LRESULT nLine = SendMessageW (_HEdit, EM_GETLINECOUNT, 0, 0) - 1;
564 if (_HistorySize > 0 && nLine+vecSize > _HistorySize)
566 int nblineremove = vecSize;
567 //nlassert (nblineremove>0 && nblineremove <= _HistorySize);
569 if (nblineremove == _HistorySize)
571 SendMessageA (_HEdit, WM_SETTEXT, 0, (LPARAM) "");
572 startSel = endSel = -1;
574 else
576 LRESULT oldIndex1 = SendMessageW (_HEdit, EM_LINEINDEX, 0, 0);
577 //nlassert (oldIndex1 != -1);
578 LRESULT oldIndex2 = SendMessageW (_HEdit, EM_LINEINDEX, nblineremove, 0);
579 //nlassert (oldIndex2 != -1);
580 SendMessageW (_HEdit, EM_SETSEL, oldIndex1, oldIndex2);
581 SendMessageW (_HEdit, EM_REPLACESEL, FALSE, (LPARAM) L"");
583 // update the selection due to the erasing
584 sint dt = (sint)(oldIndex2 - oldIndex1);
585 if (startSel < 65000)
587 if ((sint)startSel-dt < 0) startSel = -1;
588 else startSel -= dt;
590 else startSel = -1;
591 if(endSel < 65000)
593 if ((sint)endSel-dt < 0) startSel = endSel = -1;
594 else endSel -= dt;
596 else startSel = endSel = -1;
600 for (it = access.value().begin(); it != access.value().end(); )
602 ucstring str = ucstring::makeFromUtf8((*it).second);
603 uint32 col = (*it).first;
605 // get all string that have the same color
606 for (it++; it != access.value().end() && (*it).first == col; it++)
608 str += ucstring::makeFromUtf8((*it).second);
611 SendMessageA(_HEdit, EM_SETSEL, -1, -1);
613 if ((col>>24) == 0)
615 // there s a specific color
616 CharFormat.crTextColor = RGB ((col>>16)&0xFF, (col>>8)&0xFF, col&0xFF);
617 SendMessageA(_HEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &CharFormat);
620 // add the string to the edit control
621 SendMessageW(_HEdit, EM_REPLACESEL, FALSE, (LPARAM) str.c_str());
624 // restore old selection
625 SendMessageA(_HEdit, EM_SETSEL, startSel, endSel);
627 SendMessageA(_HEdit,EM_SETMODIFY,(WPARAM)TRUE,(LPARAM)0);
629 if (bottom)
630 SendMessageA(_HEdit,WM_VSCROLL,(WPARAM)SB_BOTTOM,(LPARAM)0L);
632 if (focus)
634 SendMessageA(_HEdit,EM_SETOPTIONS,ECOOP_OR,(LPARAM)ECO_AUTOVSCROLL);
635 SendMessageA(_HEdit,EM_SETOPTIONS,ECOOP_OR,(LPARAM)ECO_AUTOHSCROLL);
639 // clear the log
640 access.value().clear ();
644 // Update labels
647 updateLabels ();
650 // Manage windows message
653 MSG msg;
654 while (PeekMessageW(&msg,NULL,0,0,PM_REMOVE))
656 TranslateMessage(&msg);
657 DispatchMessageW(&msg);
661 // Wait
664 //////////////////////////////////////////////////////////////////
665 // WARNING: READ THIS !!!!!!!!!!!!!!!! ///////////////////////////
666 // If at the release time, it freezes here, it's a microsoft bug:
667 // http://support.microsoft.com/support/kb/articles/q173/2/60.asp
668 nlSleep (1);
671 DeleteObject (_HFont);
672 _HFont = NULL;
673 DestroyWindow (_HWnd);
674 _HWnd = NULL;
675 DestroyWindow (_HEdit);
676 _HEdit = NULL;
677 FreeLibrary (_HLibModule);
678 _HLibModule = NULL;
681 void CWinDisplayer::getWindowPos (uint32 &x, uint32 &y, uint32 &w, uint32 &h)
683 RECT rect;
684 // get the w and h of the client array
685 GetClientRect (_HWnd, &rect);
686 w = rect.right - rect.left;
687 h = rect.bottom - rect.top;
689 // get the x and y of the window (not the client array)
690 GetWindowRect (_HWnd, &rect);
691 x = rect.left;
692 y = rect.top;
696 } // NLMISC
698 #endif // NL_OS_WINDOWS