Linux multi-monitor fullscreen support
[ryzomcore.git] / nel / src / 3d / driver / opengl / driver_opengl_window.cpp
blob35dc433a8a93cde16e37d755635290c73487123a
1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010-2020 Winch Gate Property Limited
3 //
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2010 Robert TIMM (rti) <mail@rtti.de>
6 // Copyright (C) 2014 Laszlo KIS-ADAM (dfighter) <dfighter1985@gmail.com>
7 // Copyright (C) 2014-2020 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
8 //
9 // This program is free software: you can redistribute it and/or modify
10 // it under the terms of the GNU Affero General Public License as
11 // published by the Free Software Foundation, either version 3 of the
12 // License, or (at your option) any later version.
14 // This program is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 // GNU Affero General Public License for more details.
19 // You should have received a copy of the GNU Affero General Public License
20 // along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "stdopengl.h"
23 #include "driver_opengl.h"
24 #include "driver_opengl_extension.h"
25 #include "driver_opengl_vertex_buffer_hard.h"
27 // by default, we disable the windows menu keys (F10, ALT and ALT+SPACE key doesn't freeze or open the menu)
28 #define NL_DISABLE_MENU
30 #ifdef NL_OS_MAC
31 # import "mac/cocoa_window_delegate.h"
32 # import "mac/cocoa_application_delegate.h"
33 # import <OpenGL/OpenGL.h>
34 #elif defined (NL_OS_UNIX)
35 # ifdef HAVE_XRANDR
36 # include <X11/extensions/Xrandr.h>
37 # endif // HAVE_XRANDR
38 # ifdef HAVE_XRENDER
39 # include <X11/extensions/Xrender.h>
40 # endif // HAVE_XRENDER
41 # include <X11/Xatom.h>
42 # define _NET_WM_STATE_REMOVE 0
43 # define _NET_WM_STATE_ADD 1
44 #endif // NL_OS_UNIX
46 #include "nel/3d/u_driver.h"
47 #include "nel/misc/file.h"
49 using namespace std;
50 using namespace NLMISC;
52 #ifdef DEBUG_NEW
53 #define new DEBUG_NEW
54 #endif
56 namespace NL3D {
58 #ifdef NL_STATIC
59 #ifdef USE_OPENGLES
60 namespace NLDRIVERGLES {
61 #else
62 namespace NLDRIVERGL {
63 #endif
64 #endif
66 #ifdef NL_OS_WINDOWS
68 bool GlWndProc(CDriverGL *driver, HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
70 H_AUTO_OGL(GlWndProc)
72 if (!driver)
73 return false;
75 if(message == WM_SIZE)
77 if (driver->_CurrentMode.Windowed)
79 RECT rect;
80 GetClientRect (driver->_win, &rect);
82 // Setup gl viewport
83 driver->_CurrentMode.Width = (uint16)(rect.right-rect.left);
84 driver->_CurrentMode.Height = (uint16)(rect.bottom-rect.top);
87 else if(message == WM_MOVE)
89 if (driver->_CurrentMode.Windowed)
91 RECT rect;
92 GetWindowRect (hWnd, &rect);
93 driver->_WindowX = rect.left;
94 driver->_WindowY = rect.top;
97 else if (message == WM_ACTIVATE)
99 WORD fActive = LOWORD(wParam);
100 if (fActive == WA_INACTIVE)
102 driver->_WndActive = false;
104 else
106 driver->_WndActive = true;
109 else if ((message == WM_SETFOCUS) || (message == WM_KILLFOCUS))
111 if (driver != NULL)
113 driver->_WindowFocus = (message == WM_SETFOCUS);
117 bool trapMessage = false;
118 if (driver->_EventEmitter.getNumEmitters() > 0)
120 CWinEventEmitter *we = NLMISC::safe_cast<CWinEventEmitter *>(driver->_EventEmitter.getEmitter(0));
121 // Process the message by the emitter
122 we->setHWnd(hWnd);
123 trapMessage = we->processMessage (hWnd, message, wParam, lParam);
125 return trapMessage;
128 static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
130 H_AUTO_OGL(DriverGL_WndProc);
132 // Get the driver pointer..
133 CDriverGL *pDriver=(CDriverGL*)GetWindowLongPtr (hWnd, GWLP_USERDATA);
134 bool trapMessage = false;
135 if (pDriver != NULL)
137 trapMessage = GlWndProc (pDriver, hWnd, message, wParam, lParam);
140 // we don't want Windows to erase background
141 if (message == WM_ERASEBKGND)
143 return TRUE;
146 if (message == WM_SYSCOMMAND)
148 switch (wParam)
150 #ifdef NL_DISABLE_MENU
151 // disable menu (F10, ALT and ALT+SPACE key doesn't freeze or open the menu)
152 case SC_KEYMENU:
153 #endif // NL_DISABLE_MENU
155 // Screensaver Trying To Start?
156 case SC_SCREENSAVE:
158 // Monitor Trying To Enter Powersave?
159 case SC_MONITORPOWER:
161 // Prevent From Happening
162 return 0;
164 default:
165 break;
169 // disable menu (default ALT-F4 behavior is disabled)
170 if (message == WM_CLOSE)
172 if(pDriver && pDriver->ExitFunc)
174 pDriver->ExitFunc();
176 else
178 #ifndef NL_DISABLE_MENU
179 // if we don't disable menu, alt F4 make a direct exit else we discard the message
180 exit(0);
181 #endif // NL_DISABLE_MENU
183 return 0;
186 #ifdef WM_UNICHAR
187 // https://docs.microsoft.com/en-us/windows/win32/inputdev/wm-unichar
188 if (message == WM_UNICHAR)
189 return (wParam == UNICODE_NOCHAR);
190 #endif
192 return trapMessage ? 0 : DefWindowProcW(hWnd, message, wParam, lParam);
195 #elif defined (NL_OS_MAC)
197 bool GlWndProc(CDriverGL *driver, const void* e)
199 H_AUTO_OGL(GlWndProc)
201 if(!driver)
202 return false;
204 NSEvent* event = [NSEvent eventWithEventRef:e];
205 // NSLog(@"NSEvent in GlWndProc %@", event);
207 return driver->_EventEmitter.processMessage(event);
210 #elif defined (NL_OS_UNIX)
212 static Atom XA_WM_STATE = 0;
213 static Atom XA_WM_STATE_FULLSCREEN = 0;
214 static Atom XA_WM_ICON = 0;
215 static Atom XA_WM_WINDOW_TYPE = 0;
216 static Atom XA_WM_WINDOW_TYPE_NORMAL = 0;
217 static Atom XA_FRAME_EXTENTS = 0;
219 sint nelXErrorsHandler(Display *dpy, XErrorEvent *e)
221 char buf[80];
222 XGetErrorText(dpy, e->error_code, buf, sizeof(buf));
223 nlwarning("3D: XError: %s", buf);
224 return 1;
227 bool GlWndProc(CDriverGL *driver, XEvent &e)
229 H_AUTO_OGL(GlWndProc)
231 if (!driver)
232 return false;
234 // nlinfo("3D: glop %d %d", e.type, e.xmap.window);
236 // disable menu (default ALT-F4 behavior is disabled)
237 switch(e.type)
239 case DestroyNotify:
241 if(driver && driver->ExitFunc)
243 driver->ExitFunc();
245 else
247 #ifndef NL_DISABLE_MENU
248 // if we don't disable menu, alt F4 make a direct exit else we discard the message
249 exit(0);
250 #endif // NL_DISABLE_MENU
252 break;
254 case MapNotify:
255 driver->_WndActive = true;
256 break;
258 case UnmapNotify:
259 driver->_WndActive = false;
260 break;
262 case Expose:
263 // nlwarning("Expose event");
264 break;
266 case ConfigureNotify:
268 if (driver->_CurrentMode.Windowed && driver->_WndActive)
270 // first time setting decoration sizes
271 if ((driver->_DecorationWidth == -1) || (driver->_DecorationWidth == 0))
273 Atom type_return = 0;
274 int format_return = 0;
275 unsigned long nitems_return = 0;
276 unsigned long bytes_after_return = 0;
277 long *data = NULL;
279 int status = XGetWindowProperty(driver->_dpy, driver->_win, XA_FRAME_EXTENTS, 0, 4, False, XA_CARDINAL, &type_return, &format_return, &nitems_return, &bytes_after_return, (unsigned char**)&data);
281 // succeeded to retrieve decoration size
282 if (status == Success && type_return == XA_CARDINAL && format_return == 32 && nitems_return == 4 && data)
284 driver->_DecorationWidth = data[0];
285 driver->_DecorationHeight = data[2];
287 else
289 // use difference between current position and previous one (set by application)
290 driver->_DecorationWidth = e.xconfigure.x - driver->_WindowX;
291 driver->_DecorationHeight = e.xconfigure.y - driver->_WindowY;
294 // don't allow negative decoration sizes
295 if (driver->_DecorationWidth < 0) driver->_DecorationWidth = 0;
296 if (driver->_DecorationHeight < 0) driver->_DecorationHeight = 0;
299 driver->_CurrentMode.Width = e.xconfigure.width;
300 driver->_CurrentMode.Height = e.xconfigure.height;
301 driver->_WindowX = e.xconfigure.x - driver->_DecorationWidth;
302 driver->_WindowY = e.xconfigure.y - driver->_DecorationHeight;
305 break;
307 case FocusIn:
309 driver->_WindowFocus = true;
310 return driver->_EventEmitter.processMessage(e);
313 case FocusOut:
315 driver->_WindowFocus = false;
316 return driver->_EventEmitter.processMessage(e);
319 default:
321 // Process the message by the emitter
322 return driver->_EventEmitter.processMessage(e);
325 return true;
328 #endif // NL_OS_UNIX
330 // ***************************************************************************
331 bool CDriverGL::init (uintptr_t windowIcon, emptyProc exitFunc)
333 H_AUTO_OGL(CDriverGL_init)
335 ExitFunc = exitFunc;
337 createCursors();
339 #ifdef NL_OS_WINDOWS
340 WNDCLASSW wc;
342 if (!_Registered)
344 memset(&wc,0,sizeof(wc));
345 wc.style = CS_HREDRAW | CS_VREDRAW ;//| CS_DBLCLKS;
346 wc.lpfnWndProc = (WNDPROC)WndProc;
347 wc.cbClsExtra = 0;
348 wc.cbWndExtra = 0;
349 wc.hInstance = GetModuleHandleW(NULL);
350 wc.hIcon = (HICON)windowIcon;
351 wc.hCursor = _DefaultCursor;
352 wc.hbrBackground = WHITE_BRUSH;
353 wc.lpszClassName = L"NLClass";
354 wc.lpszMenuName = NULL;
355 if ( !RegisterClassW(&wc) )
357 return false;
359 _Registered=1;
362 // Backup monitor color parameters
363 HDC dc = CreateDCA ("DISPLAY", NULL, NULL, NULL);
364 if (dc)
366 _NeedToRestoreGammaRamp = GetDeviceGammaRamp (dc, _GammaRampBackuped) != FALSE;
368 // Release the DC
369 ReleaseDC (NULL, dc);
371 else
373 nlwarning ("(CDriverGL::init): can't create DC");
376 // ati specific : try to retrieve driver version
377 retrieveATIDriverVersion();
378 #elif defined(NL_OS_MAC)
380 nlunreferenced(windowIcon);
382 // autorelease pool for memory management
383 _autoreleasePool = [[NSAutoreleasePool alloc] init];
385 // init the application object
386 [NSApplication sharedApplication];
388 #elif defined (NL_OS_UNIX)
390 nlunreferenced(windowIcon);
392 _dpy = XOpenDisplay(NULL);
394 if (_dpy == NULL)
396 nlerror ("XOpenDisplay failed on '%s'", getenv("DISPLAY"));
398 else
400 nldebug("3D: XOpenDisplay on '%s' OK", getenv("DISPLAY"));
403 _xrandr_version = 0;
405 #ifdef HAVE_XRANDR
406 _OldSizeID = 0;
407 sint xrandr_major, xrandr_minor;
408 if (XRRQueryVersion(_dpy, &xrandr_major, &xrandr_minor))
410 _xrandr_version = xrandr_major * 100 + xrandr_minor;
411 nlinfo("3D: XRandR %d.%d found", xrandr_major, xrandr_minor);
413 #endif // HAVE_XRANDR
415 _xvidmode_version = 0;
417 #ifdef XF86VIDMODE
418 sint event = 0, error = -1, vm_major = 0, vm_minor = 0;
419 if (XF86VidModeQueryExtension(_dpy, &event, &error) && XF86VidModeQueryVersion(_dpy, &vm_major, &vm_minor))
421 _xvidmode_version = vm_major * 100 + vm_minor;
422 nlinfo("3D: XF86VidMode %d.%d found", vm_major, vm_minor);
424 #endif
426 _xrender_version = 0;
428 #ifdef HAVE_XRENDER
429 sint xrender_major, xrender_event, xrender_error;
430 if (XQueryExtension(_dpy, "RENDER", &xrender_major, &xrender_event, &xrender_error) &&
431 XRenderQueryExtension(_dpy, &xrender_event, &xrender_error))
433 sint xrender_minor = 0;
434 XRenderQueryVersion(_dpy, &xrender_major, &xrender_minor);
435 _xrender_version = xrender_major * 100 + xrender_minor;
436 nlinfo("3D: XRender %d.%d found", xrender_major, xrender_minor);
438 #endif // HAVE_XRENDER
440 nldebug("3D: Available X Extensions:");
442 if (DebugLog)
444 // list all supported extensions
445 sint nextensions = 0;
446 char **extensions = XListExtensions(_dpy, &nextensions);
448 for(sint i = 0; i < nextensions; ++i)
450 if(i%5==0) DebugLog->displayRaw("3D: ");
451 DebugLog->displayRaw(NLMISC::toString("%s ", extensions[i]).c_str());
452 if(i%5==4) DebugLog->displayRaw("\n");
455 DebugLog->displayRaw("\n");
457 XFreeExtensionList(extensions);
460 // set default X errors handler
461 XSetErrorHandler(nelXErrorsHandler);
463 // define Atoms
464 XA_WM_STATE = XInternAtom(_dpy, "_NET_WM_STATE", False);
465 XA_WM_STATE_FULLSCREEN = XInternAtom(_dpy, "_NET_WM_STATE_FULLSCREEN", False);
466 XA_WM_ICON = XInternAtom(_dpy, "_NET_WM_ICON", False);
467 XA_WM_WINDOW_TYPE = XInternAtom(_dpy, "_NET_WM_WINDOW_TYPE", False);
468 XA_WM_WINDOW_TYPE_NORMAL = XInternAtom(_dpy, "_NET_WM_WINDOW_TYPE_NORMAL", False);
469 XA_FRAME_EXTENTS = XInternAtom(_dpy, "_NET_FRAME_EXTENTS", False);
471 #endif
473 return true;
476 // ***************************************************************************
477 bool CDriverGL::unInit()
479 H_AUTO_OGL(CDriverGL_unInit)
481 if (!_CurrentMode.Windowed)
483 restoreScreenMode();
484 showCursor(true);
486 _CurrentMode.Windowed = true;
489 #ifdef NL_OS_WINDOWS
491 #ifndef USE_OPENGLES
492 // Off-screen rendering ?
493 if (_PBuffer)
495 nwglReleasePbufferDCARB(_PBuffer, _hDC);
496 nwglDestroyPbufferARB(_PBuffer);
497 _PBuffer = NULL;
499 #endif
501 if (_Registered && !UnregisterClassW(L"NLClass", GetModuleHandle(NULL)))
503 nlwarning("Can't unregister NLClass");
505 _Registered = 0;
507 // Restore monitor color parameters
508 if (_NeedToRestoreGammaRamp)
510 HDC dc = CreateDCA("DISPLAY", NULL, NULL, NULL);
511 if (dc)
513 if (!SetDeviceGammaRamp(dc, _GammaRampBackuped))
514 nlwarning("(CDriverGL::release): SetDeviceGammaRamp failed");
516 // Release the DC
517 ReleaseDC(NULL, dc);
519 else
521 nlwarning("(CDriverGL::release): can't create DC");
525 #elif defined(NL_OS_MAC)
527 [_autoreleasePool release];
529 #elif defined (NL_OS_UNIX)
531 // restore default X errors handler
532 XSetErrorHandler(NULL);
534 if (_dpy)
536 XCloseDisplay(_dpy);
537 _dpy = NULL;
540 #endif // NL_OS_UNIX
542 return true;
545 void CDriverGL::setWindowIcon(const std::vector<NLMISC::CBitmap> &bitmaps)
547 if (_win == EmptyWindow)
548 return;
550 #if defined(NL_OS_WINDOWS)
552 static HICON winIconBig = NULL;
553 static HICON winIconSmall = NULL;
555 if (winIconBig)
557 DestroyIcon(winIconBig);
558 winIconBig = NULL;
561 if (winIconSmall)
563 DestroyIcon(winIconSmall);
564 winIconSmall = NULL;
567 sint smallIndex = -1;
568 uint smallWidth = GetSystemMetrics(SM_CXSMICON);
569 uint smallHeight = GetSystemMetrics(SM_CYSMICON);
571 sint bigIndex = -1;
572 uint bigWidth = GetSystemMetrics(SM_CXICON);
573 uint bigHeight = GetSystemMetrics(SM_CYICON);
575 // find icons with the exact size
576 for(uint i = 0; i < bitmaps.size(); ++i)
578 if (smallIndex == -1 && bitmaps[i].getWidth() == smallWidth && bitmaps[i].getHeight() == smallHeight)
579 smallIndex = i;
581 if (bigIndex == -1 && bitmaps[i].getWidth() == bigWidth && bitmaps[i].getHeight() == bigHeight)
582 bigIndex = i;
585 // find icons with taller size (we will resize them)
586 for(uint i = 0; i < bitmaps.size(); ++i)
588 if (smallIndex == -1 && bitmaps[i].getWidth() >= smallWidth && bitmaps[i].getHeight() >= smallHeight)
589 smallIndex = i;
591 if (bigIndex == -1 && bitmaps[i].getWidth() >= bigWidth && bitmaps[i].getHeight() >= bigHeight)
592 bigIndex = i;
595 if (smallIndex > -1)
596 convertBitmapToIcon(bitmaps[smallIndex], winIconSmall, smallWidth, smallHeight, 32);
598 if (bigIndex > -1)
599 convertBitmapToIcon(bitmaps[bigIndex], winIconBig, bigWidth, bigHeight, 32);
601 if (winIconBig)
603 SendMessageA(_win, WM_SETICON, 0 /* ICON_SMALL */, (LPARAM)winIconSmall);
604 SendMessageA(_win, WM_SETICON, 1 /* ICON_BIG */, (LPARAM)winIconBig);
606 else
608 SendMessageA(_win, WM_SETICON, 0 /* ICON_SMALL */, (LPARAM)winIconSmall);
609 SendMessageA(_win, WM_SETICON, 1 /* ICON_BIG */, (LPARAM)winIconSmall);
612 #elif defined(NL_OS_MAC)
614 // nothing to do, on Mac OS X, only windows representing a file have icons
616 #elif defined(NL_OS_UNIX)
618 std::vector<long> icon_data;
620 if (!bitmaps.empty())
622 // process each bitmap
623 for(uint i = 0; i < bitmaps.size(); ++i)
625 convertBitmapToIcon(bitmaps[i], icon_data);
629 if (!icon_data.empty())
631 // change window icon
632 XChangeProperty(_dpy, _win, XA_WM_ICON, XA_CARDINAL, 32, PropModeReplace, (const unsigned char *) &icon_data[0], icon_data.size());
634 else
636 // delete window icon if no bitmap is available
637 XDeleteProperty(_dpy, _win, XA_WM_ICON);
640 #endif // NL_OS_WINDOWS
643 // --------------------------------------------------
644 bool CDriverGL::setDisplay(nlWindow wnd, const GfxMode &mode, bool show, bool resizeable)
646 H_AUTO_OGL(CDriverGL_setDisplay)
648 if (!mode.OffScreen)
649 NLMISC::INelContext::getInstance().setWindowedApplication(true);
651 _win = EmptyWindow;
653 _WindowVisible = false;
654 _Resizable = resizeable;
655 _DestroyWindow = false;
657 #ifdef NL_OS_WINDOWS
659 // Init pointers
660 #ifndef USE_OPENGLES
661 _PBuffer = NULL;
662 _hRC = NULL;
663 _hDC = NULL;
664 #endif
666 // Driver caps.
667 //=============
668 // Retrieve the WGL extensions before init the driver.
670 // Offscreen mode ?
671 if (_CurrentMode.OffScreen)
673 if (!createWindow(mode)) return false;
675 HWND tmpHWND = _win;
676 int width = mode.Width;
677 int height = mode.Height;
679 #ifdef USE_OPENGLES
680 // TODO: implement for OpenGL ES 1.x
681 #else
682 // resize the window
683 RECT rc;
684 SetRect (&rc, 0, 0, width, height);
685 AdjustWindowRectEx (&rc, GetWindowStyle (_win), GetMenu (_win) != NULL, GetWindowExStyle (_win));
686 SetWindowPos (_win, NULL, 0, 0, rc.right - rc.left, rc.bottom - rc.top, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE );
688 // Get the
689 HDC tempHDC = GetDC(tmpHWND);
691 _CurrentMode.Depth = uint8(GetDeviceCaps(tempHDC,BITSPIXEL));
693 // ---
694 memset(&_pfd,0,sizeof(_pfd));
695 _pfd.nSize = sizeof(_pfd);
696 _pfd.nVersion = 1;
697 _pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
698 _pfd.iPixelType = PFD_TYPE_RGBA;
699 _pfd.cColorBits = _CurrentMode.Depth;
701 // Choose best suited Depth Buffer.
702 if(_CurrentMode.Depth<=16)
704 _pfd.cDepthBits = 16;
706 else
708 _pfd.cDepthBits = 24;
709 _pfd.cAlphaBits = 8;
711 _pfd.iLayerType = PFD_MAIN_PLANE;
712 int pf=ChoosePixelFormat(tempHDC,&_pfd);
713 if (!pf)
715 nlwarning ("CDriverGL::setDisplay: ChoosePixelFormat failed");
716 DestroyWindow (tmpHWND);
717 return false;
719 if ( !SetPixelFormat(tempHDC,pf,&_pfd) )
721 nlwarning ("CDriverGL::setDisplay: SetPixelFormat failed");
722 DestroyWindow (tmpHWND);
723 return false;
726 // Create gl context
727 HGLRC tempGLRC = wglCreateContext(tempHDC);
728 if (tempGLRC == NULL)
730 DWORD error = GetLastError ();
731 nlwarning ("CDriverGL::setDisplay: wglCreateContext failed: 0x%x", error);
732 DestroyWindow (tmpHWND);
733 _PBuffer = NULL;
734 _win = EmptyWindow;
735 _hRC = NULL;
736 _hDC = NULL;
737 return false;
740 // Make the context current
741 if (!wglMakeCurrent(tempHDC,tempGLRC))
743 DWORD error = GetLastError ();
744 nlwarning ("CDriverGL::setDisplay: wglMakeCurrent failed: 0x%x", error);
745 wglDeleteContext (tempGLRC);
746 DestroyWindow (tmpHWND);
747 _PBuffer = NULL;
748 _win = EmptyWindow;
749 _hRC = NULL;
750 _hDC = NULL;
751 return false;
754 // Register WGL functions
755 #ifdef USE_OPENGLES
756 registerEGlExtensions (_Extensions, tempHDC);
757 #else
758 registerWGlExtensions (_Extensions, tempHDC);
759 #endif
761 HDC hdc = wglGetCurrentDC ();
763 if (hdc == NULL)
765 DWORD error = GetLastError ();
766 nlwarning ("CDriverGL::setDisplay: wglGetCurrentDC failed: 0x%x", error);
767 DestroyWindow (tmpHWND);
768 _PBuffer = NULL;
769 _win = EmptyWindow;
770 _hRC = NULL;
771 _hDC = NULL;
772 return false;
775 // Get ready to query for a suitable pixel format that meets our
776 // minimum requirements.
777 int iattributes[2*20];
778 float fattributes[2*20];
779 int niattribs = 0;
781 // Attribute arrays must be "0" terminated - for simplicity, first
782 // just zero-out the array then fill from left to right.
783 for ( int a = 0; a < 2*20; a++ )
785 iattributes[a] = 0;
786 fattributes[a] = 0;
789 // Since we are trying to create a pbuffer, the pixel format we
790 // request (and subsequently use) must be "buffer capable".
791 iattributes[2*niattribs ] = WGL_DRAW_TO_PBUFFER_ARB;
792 iattributes[2*niattribs+1] = true;
793 niattribs++;
795 // We require a minimum of 24-bit depth.
796 iattributes[2*niattribs ] = WGL_DEPTH_BITS_ARB;
797 iattributes[2*niattribs+1] = 24;
798 niattribs++;
800 // We require a minimum of 8-bits for each R, G, B, and A.
801 iattributes[2*niattribs ] = WGL_RED_BITS_ARB;
802 iattributes[2*niattribs+1] = 8;
803 niattribs++;
804 iattributes[2*niattribs ] = WGL_GREEN_BITS_ARB;
805 iattributes[2*niattribs+1] = 8;
806 niattribs++;
807 iattributes[2*niattribs ] = WGL_BLUE_BITS_ARB;
808 iattributes[2*niattribs+1] = 8;
809 niattribs++;
810 iattributes[2*niattribs ] = WGL_ALPHA_BITS_ARB;
811 iattributes[2*niattribs+1] = 8;
812 niattribs++;
814 // Now obtain a list of pixel formats that meet these minimum
815 // requirements.
816 int pformat[20];
817 unsigned int nformats;
818 if ( !nwglChoosePixelFormatARB ( hdc, iattributes, fattributes,
819 20, pformat, &nformats ) )
821 nlwarning ( "pbuffer creation error: Couldn't find a suitable pixel format." );
822 wglDeleteContext (tempGLRC);
823 DestroyWindow (tmpHWND);
824 return false;
827 /* After determining a compatible pixel format, the next step is to create a pbuffer of the
828 chosen format. Fortunately this step is fairly easy, as you merely select one of the formats
829 returned in the list in step #2 and call the function: */
830 int iattributes2[1] = {0};
831 // int iattributes2[] = {WGL_PBUFFER_LARGEST_ARB, 1, 0};
832 _PBuffer = nwglCreatePbufferARB( hdc, pformat[0], width, height, iattributes2 );
833 if (_PBuffer == NULL)
835 DWORD error = GetLastError ();
836 nlwarning ("CDriverGL::setDisplay: wglCreatePbufferARB failed: 0x%x", error);
837 wglDeleteContext (tempGLRC);
839 DestroyWindow (tmpHWND);
840 _PBuffer = NULL;
841 _win = EmptyWindow;
842 _hRC = NULL;
843 _hDC = NULL;
844 return false;
847 /* After creating a pbuffer, you may use this functions to determine the dimensions of the pbuffer actually created. */
848 if ( !nwglQueryPbufferARB( _PBuffer, WGL_PBUFFER_WIDTH_ARB, (int*)&width ) )
850 DWORD error = GetLastError ();
851 nlwarning ("CDriverGL::setDisplay: wglQueryPbufferARB failed: 0x%x", error);
852 wglDeleteContext (tempGLRC);
853 DestroyWindow (tmpHWND);
854 _PBuffer = NULL;
855 _win = EmptyWindow;
856 _hRC = NULL;
857 _hDC = NULL;
858 return false;
861 if ( !nwglQueryPbufferARB( _PBuffer, WGL_PBUFFER_HEIGHT_ARB, (int*)&height ) )
863 DWORD error = GetLastError ();
864 nlwarning ("CDriverGL::setDisplay: wglQueryPbufferARB failed: 0x%x", error);
865 wglDeleteContext (tempGLRC);
866 DestroyWindow (tmpHWND);
867 _PBuffer = NULL;
868 _win = EmptyWindow;
869 _hRC = NULL;
870 _hDC = NULL;
871 return false;
874 _CurrentMode.Width = width;
875 _CurrentMode.Height = height;
877 /* The next step is to create a device context for the newly created pbuffer. To do this,
878 call to the function: */
879 _hDC = nwglGetPbufferDCARB( _PBuffer );
880 if (_hDC == NULL)
882 DWORD error = GetLastError ();
883 nlwarning ("CDriverGL::setDisplay: wglGetPbufferDCARB failed: 0x%x", error);
884 nwglDestroyPbufferARB( _PBuffer );
886 wglDeleteContext (tempGLRC);
888 DestroyWindow (tmpHWND);
889 _PBuffer = NULL;
890 _win = EmptyWindow;
891 _hRC = NULL;
892 _hDC = NULL;
893 return false;
896 /* The final step of pbuffer creation is to create an OpenGL rendering context and
897 associate it with the handle for the pbuffer's device context created in step #4. This is done as follows */
898 _hRC = wglCreateContext( _hDC );
899 if (_hRC == NULL)
901 DWORD error = GetLastError ();
902 nlwarning ("CDriverGL::setDisplay: wglCreateContext failed: 0x%x", error);
903 nwglReleasePbufferDCARB( _PBuffer, _hDC );
904 nwglDestroyPbufferARB( _PBuffer );
905 wglDeleteContext (tempGLRC);
906 DestroyWindow (tmpHWND);
907 _PBuffer = NULL;
908 _win = EmptyWindow;
909 _hRC = NULL;
910 _hDC = NULL;
911 return false;
914 // Get the depth
915 _CurrentMode.Depth = uint8(GetDeviceCaps (_hDC, BITSPIXEL));
917 // Destroy the temp gl context
918 #ifdef USE_OPENGLES
919 if (!eglDestroyContext(_EglDisplay, _EglContext);)
921 DWORD error = GetLastError ();
922 nlwarning ("CDriverGL::setDisplay: wglDeleteContext failed: 0x%x", error);
924 #else
925 if (!wglDeleteContext (tempGLRC))
927 DWORD error = GetLastError ();
928 nlwarning ("CDriverGL::setDisplay: wglDeleteContext failed: 0x%x", error);
930 #endif
932 // Destroy the temp windows
933 if (!DestroyWindow (tmpHWND))
934 nlwarning ("CDriverGL::setDisplay: DestroyWindow failed");
936 /* After a pbuffer has been successfully created you can use it for off-screen rendering. To do
937 so, you'll first need to bind the pbuffer, or more precisely, make its GL rendering context
938 the current context that will interpret all OpenGL commands and state changes. */
939 if (!wglMakeCurrent(_hDC,_hRC))
941 DWORD error = GetLastError ();
942 nlwarning ("CDriverGL::setDisplay: wglMakeCurrent failed: 0x%x", error);
944 #ifdef USE_OPENGLES
945 eglDestroyContext(_EglDisplay, _EglContext);
946 #else
947 wglDeleteContext (_hRC);
948 nwglReleasePbufferDCARB( _PBuffer, _hDC );
949 nwglDestroyPbufferARB( _PBuffer );
950 #endif
952 DestroyWindow (tmpHWND);
953 _PBuffer = NULL;
954 _win = EmptyWindow;
955 _hRC = NULL;
956 _hDC = NULL;
957 return false;
959 #endif
961 else
963 if (wnd == EmptyWindow)
965 if (!createWindow(mode))
966 return false;
968 else
970 _win = wnd;
973 // associate OpenGL driver to window
974 SetWindowLongPtr(_win, GWLP_USERDATA, (LONG_PTR)this);
976 createContext();
979 /// release old emitter
980 while (_EventEmitter.getNumEmitters() != 0)
982 _EventEmitter.removeEmitter(_EventEmitter.getEmitter(_EventEmitter.getNumEmitters() - 1));
985 NLMISC::CWinEventEmitter *we = new NLMISC::CWinEventEmitter;
987 // setup the event emitter, and try to retrieve a direct input interface
988 _EventEmitter.addEmitter(we, true /*must delete*/); // the main emitter
990 #elif defined(NL_OS_MAC)
992 if (wnd == EmptyWindow)
994 if (!createWindow(mode))
995 return false;
997 else
999 _win = wnd;
1001 /* The NSView* extracted from a QWidget using winId() has bounds set to
1002 * (QWidget::x(), QWidget::y(), QWidget::width(), QWidget::height()).
1003 * This causes cocoa to draw at an offset of x(), y() leaving an unhandled
1004 * border in the NSView. The code below fixes this by translating the
1005 * coordinate system of the NSView back to 0,0.
1006 * In my opinion this is an error in Qt since QWidget::x/y() are relative to
1007 * parent and [NSView bounds.origin] is relative to it's own coordinate
1008 * system. This are incompatible notations. Qt should handle the conversion.
1009 * Fixes: #1013 Viewport size when embedding NeL Cocoa view in Qt
1010 * (http://dev.ryzom.com/issues/1013)
1012 NSView* view = (NSView*)wnd;
1013 if(view.frame.origin.x != 0 || view.frame.origin.y != 0) {
1014 [view setBoundsOrigin:view.frame.origin];
1018 // setup opengl settings
1019 NSOpenGLPixelFormatAttribute att[] =
1021 NSOpenGLPFADoubleBuffer,
1022 NSOpenGLPFAColorSize, 24,
1023 NSOpenGLPFADepthSize, 24,
1024 NSOpenGLPFAAlphaSize, 8,
1025 NSOpenGLPFAStencilSize, 8,
1026 NSOpenGLPFANoRecovery,
1027 NSOpenGLPFAAccelerated,
1028 NSOpenGLPFABackingStore,
1032 // put the settings into a format object
1033 NSOpenGLPixelFormat* format =
1034 [[NSOpenGLPixelFormat alloc] initWithAttributes:att];
1036 if(!format)
1037 nlerror("cannot create NSOpenGLPixelFormat");
1039 // intially set height/width, further updates through CocoaOpenGLView
1040 _CurrentMode.Height = mode.Height;
1041 _CurrentMode.Width = mode.Width;
1043 // create a opengl view with the created format
1044 _glView = [[CocoaOpenGLView alloc]
1045 initWithFrame:NSMakeRect(0, 0, mode.Width, mode.Height)
1046 pixelFormat:format];
1048 if(!_glView)
1049 nlerror("cannot create view");
1051 // tell the view about the driver so the view is able to update "window" size
1052 [_glView setDriver:this];
1054 // make the view automatically fit the super view
1055 [_glView setAutoresizingMask: NSViewHeightSizable | NSViewWidthSizable];
1057 // put the open gl view into the dummy view contained in the window
1058 [containerView() addSubview:_glView];
1060 // adjust size
1061 [_glView setFrame: [containerView() frame]];
1063 // create a opengl context for the view
1064 _ctx = [_glView openGLContext];
1066 if(!_ctx)
1067 nlerror("cannot create context");
1069 // free the pixel format object
1070 [format release];
1072 // let the open gl view handle the input
1073 [[containerView() window] makeFirstResponder:_glView];
1075 // prevents scrambled content in the view before first swap
1076 [_ctx flushBuffer];
1077 [_glView display];
1079 // Set context as thread context
1080 CGLSetCurrentContext((CGLContextObj)[_ctx CGLContextObj]);
1082 _EventEmitter.init(this, _glView, _DestroyWindow);
1084 #elif defined(NL_OS_UNIX)
1086 static int sAttribList16bpp[] =
1088 GLX_RGBA,
1089 GLX_DOUBLEBUFFER,
1090 GLX_DEPTH_SIZE, 16,
1091 GLX_RED_SIZE, 4,
1092 GLX_GREEN_SIZE, 4,
1093 GLX_BLUE_SIZE, 4,
1094 GLX_ALPHA_SIZE, 4,
1095 GLX_STENCIL_SIZE, 8,
1096 None
1099 static int sAttribList24bpp[] =
1101 GLX_RGBA,
1102 GLX_DOUBLEBUFFER,
1103 GLX_DEPTH_SIZE, 24,
1104 GLX_RED_SIZE, 8,
1105 GLX_GREEN_SIZE, 8,
1106 GLX_BLUE_SIZE, 8,
1107 GLX_ALPHA_SIZE, 8,
1108 GLX_STENCIL_SIZE, 8,
1109 None
1112 // first try 24bpp and if that fails 16bpp
1113 XVisualInfo *visual_info = glXChooseVisual (_dpy, DefaultScreen(_dpy), sAttribList24bpp);
1114 if (visual_info == NULL)
1115 visual_info = glXChooseVisual(_dpy, DefaultScreen(_dpy), sAttribList16bpp);
1116 if(visual_info == NULL)
1118 nlerror("glXChooseVisual() failed");
1121 _visual_info = visual_info;
1123 _ctx = glXCreateContext (_dpy, visual_info, None, GL_TRUE);
1124 if (_ctx == NULL)
1126 nlerror("glXCreateContext() failed");
1129 if (wnd == EmptyWindow)
1131 if (!createWindow(mode))
1132 return false;
1134 else
1136 _win = wnd;
1138 XSetWindowAttributes attr;
1139 attr.background_pixel = BlackPixel(_dpy, DefaultScreen(_dpy));
1140 attr.override_redirect = False;
1142 int attr_flags = CWOverrideRedirect | CWBackPixel;
1144 XChangeWindowAttributes(_dpy, _win, attr_flags, &attr);
1147 glXMakeCurrent (_dpy, _win, _ctx);
1148 // XMapRaised (_dpy, _win);
1150 // XMapWindow(_dpy, _win);
1152 _EventEmitter.init (_dpy, _win, this);
1154 // XEvent event;
1155 // XIfEvent(dpy, &event, WaitForNotify, (char *)this);
1157 #endif // NL_OS_UNIX
1159 // setup OpenGL structures
1160 if (!setupDisplay())
1161 return false;
1163 // setup window size and screen mode
1164 if (!setMode(mode))
1165 return false;
1167 if (show || !_CurrentMode.Windowed)
1168 showWindow(true);
1170 return true;
1173 // --------------------------------------------------
1174 bool CDriverGL::saveScreenMode()
1176 H_AUTO_OGL(CDriverGL_saveScreenMode)
1178 bool res = true;
1180 #if defined(NL_OS_WINDOWS)
1182 // don't need to save it because Windows will use default desktop resolution
1184 #elif defined(NL_OS_MAC)
1186 // no need to store because the screen mode is never really changed
1188 #elif defined(NL_OS_UNIX)
1190 int screen = DefaultScreen(_dpy);
1191 res = false;
1193 #ifdef HAVE_XRANDR
1195 // TODO: if using mode switching, save current xrandr mode to _OldSizeID
1196 res = true;
1197 if (!res && _xrandr_version > 0)
1199 XRRScreenConfiguration *screen_config = XRRGetScreenInfo(_dpy, RootWindow(_dpy, screen));
1201 if (screen_config)
1203 Rotation saved_rotation;
1204 _OldSizeID = XRRConfigCurrentConfiguration(screen_config, &saved_rotation);
1205 nlinfo("3D: current XRandR mode %d", _OldSizeID);
1206 XRRFreeScreenConfigInfo(screen_config);
1208 res = true;
1210 else
1212 nlwarning("3D: XRRGetScreenInfo failed");
1216 #endif // HAVE_XRANDR
1218 #if defined(XF86VIDMODE)
1220 if (!res && _xvidmode_version > 0)
1222 // Store old mode in order to restore it when leaving fullscreen
1223 memset(&_OldScreenMode, 0, sizeof(XF86VidModeModeLine));
1224 XF86VidModeGetModeLine(_dpy, screen, &_OldDotClock, &_OldScreenMode);
1225 res = XF86VidModeGetViewPort(_dpy, screen, &_OldX, &_OldY);
1228 #endif // XF86VIDMODE
1230 #endif // NL_OS_WINDOWS
1232 return res;
1235 // --------------------------------------------------
1236 bool CDriverGL::restoreScreenMode()
1238 H_AUTO_OGL(CDriverGL_restoreScreenMode)
1240 bool res = false;
1242 #if defined(NL_OS_WINDOWS)
1244 res = (ChangeDisplaySettings(NULL, 0) == DISP_CHANGE_SUCCESSFUL);
1246 #elif defined(NL_OS_MAC)
1248 // no need to restore because the screen mode was never really changed
1249 res = true;
1251 #elif defined(NL_OS_UNIX)
1253 int screen = DefaultScreen(_dpy);
1255 #ifdef HAVE_XRANDR
1257 // TODO: if using mode switching, then restore mode from _OldSizeID
1258 res = true;
1259 if (!res && _xrandr_version > 0)
1261 Window root = RootWindow(_dpy, screen);
1263 XRRScreenConfiguration *screen_config = XRRGetScreenInfo(_dpy, root);
1265 if (screen_config)
1267 Rotation saved_rotation;
1268 SizeID size = XRRConfigCurrentConfiguration(screen_config, &saved_rotation);
1269 if (XRRSetScreenConfig(_dpy, screen_config, root, _OldSizeID, saved_rotation, CurrentTime) == RRSetConfigSuccess)
1271 nlinfo("3D: Switching back to XRandR mode %d", _OldSizeID);
1272 res = true;
1275 XRRFreeScreenConfigInfo(screen_config);
1277 else
1279 nlwarning("3D: XRRGetScreenInfo failed");
1283 #endif // HAVE_XRANDR
1285 #if defined(XF86VIDMODE)
1287 if (!res && _xvidmode_version > 0)
1289 XF86VidModeModeInfo info;
1290 nlinfo("3D: Switching back to original mode");
1292 // This is UGLY
1293 info.dotclock = _OldDotClock;
1294 info.hdisplay = _OldScreenMode.hdisplay;
1295 info.hsyncstart = _OldScreenMode.hsyncstart;
1296 info.hsyncend = _OldScreenMode.hsyncend;
1297 info.htotal = _OldScreenMode.htotal;
1298 info.vdisplay = _OldScreenMode.vdisplay;
1299 info.vsyncstart = _OldScreenMode.vsyncstart;
1300 info.vsyncend = _OldScreenMode.vsyncend;
1301 info.vtotal = _OldScreenMode.vtotal;
1302 info.flags = _OldScreenMode.flags;
1303 info.privsize = _OldScreenMode.privsize;
1304 info.c_private = _OldScreenMode.c_private;
1306 nlinfo("3D: Switching back mode to %dx%d", info.hdisplay, info.vdisplay);
1307 XF86VidModeSwitchToMode(_dpy, screen, &info);
1308 nlinfo("3D: Switching back viewport to %d,%d",_OldX, _OldY);
1309 res = XF86VidModeSetViewPort(_dpy, screen, _OldX, _OldY);
1312 #endif // XF86VIDMODE
1314 #endif // NL_OS_WINDOWS
1316 return res;
1319 // ***************************************************************************
1321 #ifdef XF86VIDMODE
1322 static sint modeInfoToFrequency(XF86VidModeModeInfo *info)
1324 return (info->htotal && info->vtotal) ? (1000 * info->dotclock / (info->htotal * info->vtotal)) : 0;
1326 #endif // XF86VIDMODE
1328 // ***************************************************************************
1330 bool CDriverGL::setScreenMode(const GfxMode &mode)
1332 H_AUTO_OGL(CDriverGL_setScreenMode)
1334 if (mode.Windowed)
1336 // if fullscreen, switch back to desktop screen mode
1337 if (!_CurrentMode.Windowed)
1338 restoreScreenMode();
1340 return true;
1343 // save previous screen mode only if switching from windowed to fullscreen
1344 if (_CurrentMode.Windowed)
1345 saveScreenMode();
1347 // if switching exactly to the same screen mode, doesn't change it
1348 GfxMode previousMode;
1349 if (getCurrentScreenMode(previousMode)
1350 && mode.Width == previousMode.Width
1351 && mode.Height == previousMode.Height
1352 && mode.Depth == previousMode.Depth
1353 && mode.Frequency == previousMode.Frequency
1354 && mode.DisplayDevice == previousMode.DisplayDevice)
1355 return true;
1357 #if defined(NL_OS_WINDOWS)
1359 DEVMODE devMode;
1360 memset(&devMode, 0, sizeof(DEVMODE));
1361 devMode.dmSize = sizeof(DEVMODE);
1362 devMode.dmDriverExtra = 0;
1363 devMode.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT;
1364 devMode.dmPelsWidth = mode.Width;
1365 devMode.dmPelsHeight = mode.Height;
1367 if(mode.Depth > 0)
1369 devMode.dmBitsPerPel = mode.Depth;
1370 devMode.dmFields |= DM_BITSPERPEL;
1373 if(mode.Frequency > 0)
1375 devMode.dmDisplayFrequency = mode.Frequency;
1376 devMode.dmFields |= DM_DISPLAYFREQUENCY;
1379 if (ChangeDisplaySettings(&devMode, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL)
1381 nlwarning("3D: Fullscreen mode switch failed");
1382 return false;
1385 #elif defined(NL_OS_MAC)
1387 // no need to do anything here, on mac os, the screen mode is never changed
1389 #elif defined(NL_OS_UNIX)
1391 bool found = false;
1393 #ifdef HAVE_XRANDR
1394 // TODO: implement mode switching using xrandr crts
1395 found = true;
1397 if (!found && _xrandr_version > 0)
1399 int screen = DefaultScreen(_dpy);
1400 Window root = RootWindow(_dpy, screen);
1402 XRRScreenConfiguration *screen_config = XRRGetScreenInfo(_dpy, root);
1404 if (screen_config)
1406 Rotation saved_rotation;
1407 SizeID cur_size = XRRConfigCurrentConfiguration(screen_config, &saved_rotation);
1409 sint nsizes;
1410 XRRScreenSize *sizes = XRRConfigSizes(screen_config, &nsizes);
1411 sint size = -1;
1413 for (sint i = 0; i < nsizes; ++i)
1415 if (sizes[i].width == mode.Width && sizes[i].height == mode.Height)
1417 size = i;
1418 break;
1422 if (size > -1)
1424 if (XRRSetScreenConfig(_dpy, screen_config, root, size, saved_rotation, CurrentTime) == RRSetConfigSuccess)
1426 nlinfo("3D: Switching to XRandR mode %d: %dx%d", size, sizes[size].width, sizes[size].height);
1427 found = true;
1429 else
1431 nlwarning("3D: XRRSetScreenConfig failed for mode %d: %dx%d", size, sizes[size].width, sizes[size].height);
1434 else
1436 nlwarning("3D: No corresponding screen mode");
1439 XRRFreeScreenConfigInfo(screen_config);
1441 else
1443 nlwarning("3D: XRRGetScreenInfo failed");
1447 #endif // HAVE_XRANDR
1449 #if defined(XF86VIDMODE)
1451 if (!found && _xvidmode_version > 0)
1453 // Find the requested mode and use it
1454 XF86VidModeModeInfo **modes;
1455 int nmodes;
1456 if (XF86VidModeGetAllModeLines(_dpy, DefaultScreen(_dpy), &nmodes, &modes))
1458 for (int i = 0; i < nmodes; i++)
1460 const uint16 freq = modeInfoToFrequency(modes[i]);
1462 nldebug("3D: Available mode - %dx%d %d Hz", modes[i]->hdisplay, modes[i]->vdisplay, (int)freq);
1463 if (modes[i]->hdisplay == mode.Width && modes[i]->vdisplay == mode.Height /* && freq == mode.Frequency */)
1465 if (XF86VidModeSwitchToMode(_dpy, DefaultScreen(_dpy), modes[i]))
1467 nlinfo("3D: XF86VidMode Switching to mode %dx%d", modes[i]->hdisplay, modes[i]->vdisplay);
1468 XF86VidModeSetViewPort(_dpy, DefaultScreen(_dpy), 0, 0);
1469 found = true;
1471 break;
1474 XFree(modes);
1478 #endif // XF86VIDMODE
1480 if (!found)
1481 return false;
1483 #endif // NL_OS_WINDOWS
1485 return true;
1488 // ***************************************************************************
1489 bool CDriverGL::createWindow(const GfxMode &mode)
1491 H_AUTO_OGL(CDriverGL_createWindow)
1493 nlWindow window = EmptyWindow;
1495 #ifdef NL_OS_WINDOWS
1497 // create the OpenGL window
1498 DWORD dwStyle = WS_OVERLAPPEDWINDOW|WS_CLIPCHILDREN|WS_CLIPSIBLINGS;
1499 int pos = CW_USEDEFAULT;
1500 HWND hwndParent = HWND_DESKTOP;
1501 if (mode.OffScreen)
1503 dwStyle &= ~WS_VISIBLE;
1504 pos = 0;
1505 hwndParent = NULL;
1507 window = CreateWindowW(L"NLClass", L"NeL Window", dwStyle,
1508 pos, pos, mode.Width, mode.Height, hwndParent, NULL, GetModuleHandle(NULL), NULL);
1510 if (window == EmptyWindow)
1512 DWORD res = GetLastError();
1513 nlwarning("CreateWindow failed: %u", res);
1514 return false;
1517 #elif defined(NL_OS_MAC)
1519 // create the menu in the top screen bar
1520 setupApplicationMenu();
1522 // finish the application launching
1523 [NSApp finishLaunching];
1525 // describe how the window should look like and behave
1526 unsigned int styleMask = NSTitledWindowMask | NSClosableWindowMask |
1527 NSMiniaturizableWindowMask | NSResizableWindowMask;
1529 // create a cocoa window with the size provided by the mode parameter
1530 NSWindow* cocoa_window = [[NSWindow alloc]
1531 initWithContentRect:NSMakeRect(0, 0, mode.Width, mode.Height)
1532 styleMask:styleMask backing:NSBackingStoreBuffered defer:NO];
1534 if(!cocoa_window)
1536 nlerror("cannot create cocoa window");
1537 return false;
1540 // create an application delegate
1541 CocoaApplicationDelegate* appDelegate =
1542 [[CocoaApplicationDelegate alloc] initWithDriver:this];
1544 // set the application delegate, this will handle window/app close events
1545 [NSApp setDelegate:(id<NSApplicationDelegate>)appDelegate];
1547 // bind the close button of the window to applicationShouldTerminate
1548 id closeButton = [cocoa_window standardWindowButton:NSWindowCloseButton];
1549 [closeButton setAction:@selector(applicationShouldTerminate:)];
1550 [closeButton setTarget:appDelegate];
1552 // set the delegate which will handle window move events
1553 [cocoa_window setDelegate:[[CocoaWindowDelegate alloc] initWithDriver:this]];
1555 // set the window to non transparent
1556 [cocoa_window setOpaque:YES];
1558 // enable mouse move events, NeL wants them
1559 [cocoa_window setAcceptsMouseMovedEvents:YES];
1561 // there are no overlapping subviews, can use the magical optimization :)
1562 [cocoa_window useOptimizedDrawing:YES];
1564 // put the window to the front and make it the key window
1565 [cocoa_window makeKeyAndOrderFront:nil];
1567 // this is our main window
1568 [cocoa_window makeMainWindow];
1570 // create a dummy view which works like the window on other platforms
1571 // the open gl view will be created as subview of this one.
1572 window = [[NSView alloc]
1573 initWithFrame:NSMakeRect(0, 0, mode.Width, mode.Height)];
1575 [cocoa_window setContentView: (NSView*)window];
1577 if(window == EmptyWindow)
1579 nldebug("cannot create cocoa view for cocoa window");
1580 return false;
1583 #elif defined (NL_OS_UNIX)
1585 if (_visual_info == NULL)
1586 return false;
1588 nlWindow root = RootWindow(_dpy, DefaultScreen(_dpy));
1590 XSetWindowAttributes attr;
1591 attr.background_pixel = BlackPixel(_dpy, DefaultScreen(_dpy));
1592 attr.colormap = XCreateColormap(_dpy, root, _visual_info->visual, AllocNone);
1593 int attr_flags = CWBackPixel | CWColormap;
1595 window = XCreateWindow (_dpy, root, 0, 0, mode.Width, mode.Height, 0, _visual_info->depth, InputOutput, _visual_info->visual, attr_flags, &attr);
1597 if (window == EmptyWindow)
1599 nlerror("3D: XCreateWindow() failed");
1600 return false;
1603 // normal window type
1604 XChangeProperty(_dpy, window, XA_WM_WINDOW_TYPE, XA_ATOM, 32, PropModeReplace, (const unsigned char*)&XA_WM_WINDOW_TYPE_NORMAL, 1);
1606 // set WM hints
1607 XWMHints *wm_hints = XAllocWMHints();
1609 if (wm_hints)
1611 wm_hints->flags = StateHint | InputHint;
1612 wm_hints->initial_state = NormalState;
1613 wm_hints->input = True;
1615 XSetWMHints(_dpy, window, wm_hints);
1616 XFree(wm_hints);
1618 else
1620 nlwarning("3D: Couldn't allocate XWMHints");
1623 // set class hints
1624 XClassHint *class_hints = XAllocClassHint();
1626 if (class_hints)
1628 class_hints->res_name = (char*)"NeL";
1629 class_hints->res_class = (char*)"nel";
1631 XSetClassHint(_dpy, window, class_hints);
1632 XFree(class_hints);
1634 else
1636 nlwarning("3D: Couldn't allocate XClassHint");
1639 #endif // NL_OS_UNIX
1641 _win = window;
1643 _CurrentMode.Width = mode.Width;
1644 _CurrentMode.Height = mode.Height;
1646 // Must destroy this window
1647 _DestroyWindow = true;
1649 setWindowTitle(ucstring("NeL window"));
1651 createCursors();
1653 return true;
1656 // ***************************************************************************
1658 bool CDriverGL::destroyWindow()
1660 H_AUTO_OGL(CDriverGL_destroyWindow)
1662 releaseCursors();
1664 // make sure window icons are deleted
1665 std::vector<NLMISC::CBitmap> bitmaps;
1666 setWindowIcon(bitmaps);
1668 #ifdef USE_OPENGLES
1670 if (_EglDisplay && _EglContext)
1672 eglMakeCurrent(_EglDisplay, _EglSurface, _EglSurface, _EglContext);
1674 if (_DestroyWindow)
1676 eglDestroyContext(_EglDisplay, _EglContext);
1680 #elif defined(NL_OS_WINDOWS)
1682 // Then delete.
1683 // wglMakeCurrent(NULL,NULL);
1685 if (_hDC)
1686 wglMakeCurrent(_hDC, NULL);
1688 if (_hRC)
1690 wglDeleteContext(_hRC);
1691 _hRC = NULL;
1694 if (_win && _hDC)
1696 ReleaseDC(_win, _hDC);
1697 _hDC = NULL;
1700 #elif defined(NL_OS_MAC)
1702 #elif defined(NL_OS_UNIX)
1704 if (_DestroyWindow && _ctx) // FIXME: _DestroyWindow may need to be removed here as well
1705 glXDestroyContext(_dpy, _ctx);
1707 _ctx = NULL;
1709 #endif
1711 #ifdef NL_OS_WINDOWS
1713 // don't destroy window if it hasn't been created by our driver
1714 if (_win && _DestroyWindow)
1715 DestroyWindow(_win);
1717 #elif defined(NL_OS_MAC)
1719 if (_DestroyWindow)
1721 [[containerView() window] release];
1722 [containerView() release];
1723 [_glView release];
1726 _ctx = nil;
1728 #elif defined (NL_OS_UNIX)
1730 _EventEmitter.closeIM();
1732 if (_DestroyWindow && _win)
1733 XDestroyWindow(_dpy, _win);
1735 // Ungrab the keyboard (probably not necessary);
1736 // XUnmapWindow(_dpy, _win);
1737 XSync(_dpy, True);
1738 XUngrabKeyboard(_dpy, CurrentTime);
1740 #endif
1742 _win = EmptyWindow;
1744 return true;
1747 // ***************************************************************************
1749 CDriverGL::EWindowStyle CDriverGL::getWindowStyle() const
1751 H_AUTO_OGL(CDriverGL_getWindowStyle)
1753 if (!_CurrentMode.Windowed)
1754 return EWSFullscreen;
1756 return EWSWindowed;
1759 // ***************************************************************************
1761 bool CDriverGL::setWindowStyle(EWindowStyle windowStyle)
1763 H_AUTO_OGL(CDriverGL_setWindowStyle)
1765 // don't change window style, if we did not create the window
1766 if (_win == EmptyWindow || !_DestroyWindow)
1767 return true;
1769 if (getWindowStyle() == windowStyle)
1770 return true;
1772 #if defined(NL_OS_WINDOWS)
1774 // get current style
1775 LONG dwStyle = GetWindowLong(_win, GWL_STYLE);
1777 // prepare new style
1778 LONG dwNewStyle = WS_CLIPCHILDREN|WS_CLIPSIBLINGS;
1780 // get window current state
1781 WINDOWPLACEMENT wndpl;
1782 wndpl.length = sizeof(WINDOWPLACEMENT);
1784 bool isMaximized = GetWindowPlacement(_win, &wndpl) && (wndpl.showCmd == SW_SHOWMAXIMIZED);
1785 bool isVisible = false;
1787 if (windowStyle == EWSWindowed && !_CurrentMode.OffScreen)
1789 dwNewStyle |= WS_OVERLAPPEDWINDOW;
1791 // if we can't resize window, remove maximize box and resize anchors
1792 if (!_Resizable) dwNewStyle ^= WS_MAXIMIZEBOX|WS_THICKFRAME;
1794 isVisible = (dwStyle & WS_VISIBLE) != 0;
1796 else if (windowStyle == EWSFullscreen)
1798 #ifndef _DEBUG
1799 dwNewStyle |= WS_POPUP;
1800 #endif
1801 isVisible = true;
1804 if (isVisible)
1805 dwNewStyle |= WS_VISIBLE;
1807 if (dwStyle != dwNewStyle)
1808 SetWindowLong(_win, GWL_STYLE, dwNewStyle);
1810 // if (windowStyle == EWSMaximized && isVisible && !isMaximized)
1811 // ShowWindow(_hWnd, SW_SHOWMAXIMIZED);
1812 // else if (isMaximized && isVisible)
1813 // ShowWindow(_hWnd, SW_RESTORE);
1815 #elif defined(NL_OS_MAC)
1817 // leave fullscreen mode, enter windowed mode
1818 if (windowStyle == EWSWindowed && [containerView() isInFullScreenMode])
1820 // disable manual setting of back buffer size, cocoa handles this
1821 // automatically as soon as the view gets resized
1822 CGLError error = CGLDisable((CGLContextObj)[_ctx CGLContextObj],
1823 kCGLCESurfaceBackingSize);
1825 if (error != kCGLNoError)
1826 nlerror("cannot disable kCGLCESurfaceBackingSize (%s)",
1827 CGLErrorString(error));
1829 // pull the view back from fullscreen restoring window options
1830 [containerView() exitFullScreenModeWithOptions:nil];
1832 // let the gl view receive key events
1833 [[containerView() window] makeFirstResponder:_glView];
1835 // bring the window containing the gl view to the front
1836 [[containerView() window] makeKeyAndOrderFront:nil];
1839 // enter fullscreen, leave windowed mode
1840 else if (windowStyle == EWSFullscreen && ![containerView() isInFullScreenMode])
1842 // enable manual back buffer size for mode setting in fullscreen
1843 CGLError error = CGLEnable((CGLContextObj)[_ctx CGLContextObj],
1844 kCGLCESurfaceBackingSize);
1846 if (error != kCGLNoError)
1847 nlerror("cannot enable kCGLCESurfaceBackingSize (%s)",
1848 CGLErrorString(error));
1850 // put the view in fullscreen mode, hiding the dock but enabling the menubar
1851 // to pop up if the mouse hits the top screen border.
1852 // NOTE: withOptions:nil disables <CMD>+<Tab> application switching!
1853 #ifdef NL_MAC_VERSION_10_6_UP
1854 [containerView() enterFullScreenMode:[NSScreen mainScreen] withOptions:
1855 [NSDictionary dictionaryWithObjectsAndKeys:
1856 [NSNumber numberWithInt:
1857 NSApplicationPresentationHideDock |
1858 NSApplicationPresentationAutoHideMenuBar],
1859 NSFullScreenModeApplicationPresentationOptions, nil]];
1860 #endif
1862 TODO check if simply using NSView enterFullScreenMode is a good idea.
1863 the context can be set to full screen as well, performance differences?
1866 // let the gl view receive key events
1867 [[containerView() window] makeFirstResponder:_glView];
1870 #elif defined(NL_OS_UNIX)
1872 XWindowAttributes attr;
1873 XGetWindowAttributes(_dpy, _win, &attr);
1875 // if window is mapped use events else properties
1876 if (attr.map_state != IsUnmapped)
1878 // Toggle fullscreen
1879 XEvent xev;
1880 xev.xclient.type = ClientMessage;
1881 xev.xclient.serial = 0;
1882 xev.xclient.send_event = True;
1883 xev.xclient.display = _dpy;
1884 xev.xclient.window = _win;
1885 xev.xclient.message_type = XA_WM_STATE;
1886 xev.xclient.format = 32;
1887 xev.xclient.data.l[0] = windowStyle == EWSFullscreen ? _NET_WM_STATE_ADD:_NET_WM_STATE_REMOVE;
1888 xev.xclient.data.l[1] = XA_WM_STATE_FULLSCREEN;
1889 xev.xclient.data.l[2] = 0;
1890 xev.xclient.data.l[3] = 1; // 1 for Application, 2 for Page or Taskbar, 0 for old source
1891 xev.xclient.data.l[4] = 0;
1892 if (!XSendEvent(_dpy, XDefaultRootWindow(_dpy), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev))
1894 nlwarning("3D: Failed to toggle to fullscreen");
1895 return false;
1898 else
1900 if (windowStyle == EWSFullscreen)
1902 // set state property to fullscreen
1903 XChangeProperty(_dpy, _win, XA_WM_STATE, XA_ATOM, 32, PropModeReplace, (const unsigned char*)&XA_WM_STATE_FULLSCREEN, 1);
1905 else
1907 // delete state property
1908 XDeleteProperty(_dpy, _win, XA_WM_STATE);
1912 #endif // NL_OS_WINDOWS
1914 _CurrentMode.Windowed = (windowStyle == EWSWindowed);
1916 return true;
1919 bool CDriverGL::getMonitorByName(const std::string &name, sint32 &x, sint32 &y, uint32 &w, uint32 &h) const
1921 bool found = false;
1923 #if HAVE_XRANDR
1924 int screen = DefaultScreen(_dpy);
1926 // xrandr 1.5+
1927 if (_xrandr_version >= 105)
1929 int nmonitors = 0;
1930 XRRMonitorInfo *monitor = XRRGetMonitors(_dpy, RootWindow(_dpy, screen), 1, &nmonitors);
1931 if (!monitor)
1932 return false;
1934 for(sint i = 0; i< nmonitors; ++i)
1936 char* pname = XGetAtomName(_dpy, monitor[i].name);
1937 found = (nlstricmp(pname, name) == 0);
1938 XFree(pname);
1940 if (found)
1942 x = monitor[i].x;
1943 y = monitor[i].y;
1944 w = monitor[i].width;
1945 h = monitor[i].height;
1947 break;
1951 XRRFreeMonitors(monitor);
1953 else
1955 XRRScreenResources *resources = XRRGetScreenResourcesCurrent(_dpy, RootWindow(_dpy, screen));
1956 if (!resources)
1957 resources = XRRGetScreenResources(_dpy, RootWindow(_dpy, screen));
1959 for(uint i = 0; i < resources->noutput; ++i)
1961 XRROutputInfo *output = XRRGetOutputInfo(_dpy, resources, resources->outputs[i]);
1962 if (!output)
1963 continue;
1965 if (output->crtc && output->connection == RR_Connected && nlstricmp(name, output->name) == 0)
1967 // physical monitor
1968 XRRCrtcInfo *crtc = XRRGetCrtcInfo(_dpy, resources, output->crtc);
1969 if (crtc)
1971 found = true;
1972 x = crtc->x;
1973 y = crtc->y;
1975 // TODO: test rotation
1976 if (crtc->rotation == RR_Rotate_0 || crtc->rotation == RR_Rotate_180)
1978 w = crtc->width;
1979 h = crtc->height;
1981 else
1983 w = crtc->height;
1984 h = crtc->width;
1987 XRRFreeCrtcInfo(crtc);
1991 XRRFreeOutputInfo(output);
1993 if (found)
1994 break;
1997 XRRFreeScreenResources(resources);
1999 #endif
2000 return found;
2003 // --------------------------------------------------
2004 bool CDriverGL::setMode(const GfxMode& amode)
2006 H_AUTO_OGL(CDriverGL_setMode);
2008 // don't modify window or screen if managed by a 3rd party library
2009 if (!_DestroyWindow)
2010 return true;
2012 #if !(HAVE_XRANDR)
2013 const GfxMode &mode = amode;
2014 #endif
2016 #if defined(NL_OS_WINDOWS)
2017 // save relative cursor
2018 POINT cursorPos;
2019 cursorPos.x = 0;
2020 cursorPos.y = 0;
2022 BOOL cursorPosOk = isSystemCursorInClientArea()
2023 && GetCursorPos(&cursorPos)
2024 && ScreenToClient(_win, &cursorPos);
2026 // FIXME: this probably needs to use _CurrentMode instead of mode
2027 sint curX = (sint)cursorPos.x * (sint)mode.Width;
2028 sint curY = (sint)cursorPos.y * (sint)mode.Height;
2029 #endif
2031 #if HAVE_XRANDR
2032 GfxMode mode = amode;
2034 if (!mode.Windowed)
2036 GfxMode current;
2037 if (!getCurrentScreenMode(current))
2038 nlinfo("3D: XrandR: Reading active monitor info failed");
2040 sint newX = _WindowX;
2041 sint newY = _WindowY;
2043 // make sure resolution matches requested or currently active monitor's resolution
2044 mode.Width = current.Width;
2045 mode.Height = current.Height;
2046 if (!mode.DisplayDevice.empty())
2048 uint newW = current.Width;
2049 uint newH = current.Height;
2050 if (getMonitorByName(mode.DisplayDevice, newX, newY, newW, newH))
2052 mode.Width = newW;
2053 mode.Height = newH;
2055 else
2057 nlinfo("3D: XrandR: Reading requested monitor '%s' info failed, using '%s'", mode.DisplayDevice.c_str(), current.DisplayDevice.c_str());
2058 mode.DisplayDevice = current.DisplayDevice;
2062 // switching monitors.
2063 // first move mouse pointer to target monitor and then move window.
2064 // if window is visible, then also restore mouse relative position.
2065 if (!mode.DisplayDevice.empty() && mode.DisplayDevice != current.DisplayDevice)
2067 int screen = DefaultScreen(_dpy);
2068 Window root = RootWindow(_dpy, screen);
2069 uint mouseX = mode.Width / 2;
2070 uint mouseY = mode.Height / 2;
2072 XWindowAttributes xwa;
2073 XGetWindowAttributes(_dpy, _win, &xwa);
2074 if (xwa.map_state != IsUnmapped)
2076 Window root_win;
2077 Window child_win;
2078 sint root_x, root_y, win_x, win_y;
2079 uint mask;
2081 Bool res = XQueryPointer(_dpy, _win, &root_win, &child_win, &root_x, &root_y, &win_x, &win_y, &mask);
2082 if (res)
2084 mouseX = (uint)((float)win_x * mode.Width / current.Width);
2085 mouseY = (uint)((float)win_y * mode.Height / current.Height);
2089 XWarpPointer(_dpy, None, root, None, None, None, None, newX + mouseX, newY + mouseY);
2090 XMoveWindow(_dpy, _win, newX, newY);
2091 _WindowX = newX;
2092 _WindowY = newY;
2095 #endif
2097 if (!setScreenMode(mode))
2098 return false;
2100 _CurrentMode.Depth = mode.Depth;
2101 _CurrentMode.Frequency = mode.Frequency;
2102 _CurrentMode.DisplayDevice = mode.DisplayDevice;
2104 // when changing window style, it's possible system change window size too
2105 setWindowStyle(mode.Windowed ? EWSWindowed : EWSFullscreen);
2106 setWindowPos(_WindowX, _WindowY);
2107 setWindowSize(mode.Width, mode.Height);
2109 switch (_CurrentMode.Depth)
2111 case 16: _ColorDepth = ColorDepth16; break;
2112 case 24:
2113 case 32: _ColorDepth = ColorDepth32; break;
2116 #if defined(NL_OS_WINDOWS)
2117 // restore relative cursor
2118 if (cursorPosOk)
2120 cursorPos.x = curX / (sint)mode.Width;
2121 cursorPos.y = curY / (sint)mode.Height;
2122 ClientToScreen(_win, &cursorPos);
2123 SetCursorPos(cursorPos.x, cursorPos.y);
2125 #endif
2127 // set color depth for custom cursor
2128 updateCursor(true);
2130 return true;
2133 #ifdef NL_OS_MAC
2135 #ifdef NL_MAC_VERSION_10_6_UP
2137 /// helper to extract bits per pixel value from screen mode, only 16 or 32 bits
2138 static int bppFromDisplayMode(CGDisplayModeRef mode)
2140 CFStringRef pixelEncoding = CGDisplayModeCopyPixelEncoding(mode);
2142 if(CFStringCompare(pixelEncoding, CFSTR(IO32BitDirectPixels),
2143 kCFCompareCaseInsensitive) == kCFCompareEqualTo)
2144 return 32;
2146 else if(CFStringCompare(pixelEncoding, CFSTR(IO16BitDirectPixels),
2147 kCFCompareCaseInsensitive) == kCFCompareEqualTo)
2148 return 16;
2150 return 0;
2153 #else
2155 long GetDictionaryLong(CFDictionaryRef theDict, const void* key)
2157 long value = 0;
2158 CFNumberRef numRef;
2159 numRef = (CFNumberRef)CFDictionaryGetValue(theDict, key);
2160 if (numRef != NULL)
2161 CFNumberGetValue(numRef, kCFNumberLongType, &value);
2162 return value;
2165 // some macros to make code more readable.
2166 #define GetModeWidth(mode) GetDictionaryLong((mode), kCGDisplayWidth)
2167 #define GetModeHeight(mode) GetDictionaryLong((mode), kCGDisplayHeight)
2168 #define GetModeBitsPerPixel(mode) GetDictionaryLong((mode), kCGDisplayBitsPerPixel)
2170 #endif
2172 #endif // NL_OS_MAC
2174 // --------------------------------------------------
2175 bool CDriverGL::getModes(std::vector<GfxMode> &modes)
2177 H_AUTO_OGL(CDriverGL_getModes)
2179 #ifdef NL_OS_WINDOWS
2180 sint modeIndex = 0;
2181 DEVMODE devMode;
2182 while (EnumDisplaySettings (NULL, modeIndex, &devMode))
2184 // Keep only 16 and 32 bits
2185 if ((devMode.dmBitsPerPel == 16 ) || (devMode.dmBitsPerPel == 32))
2187 // Add this mode
2188 GfxMode mode;
2189 mode.Width = (uint16)devMode.dmPelsWidth;
2190 mode.Height = (uint16)devMode.dmPelsHeight;
2191 mode.Depth = (uint8)devMode.dmBitsPerPel;
2192 mode.Frequency = devMode.dmDisplayFrequency;
2193 modes.push_back (mode);
2196 // Mode index
2197 modeIndex++;
2200 #elif defined(NL_OS_MAC)
2202 static const CGDisplayCount kMaxDisplays = 16;
2203 CGDirectDisplayID display[kMaxDisplays];
2204 CGDisplayCount numDisplays;
2206 CGDisplayErr err = CGGetActiveDisplayList(kMaxDisplays, display, &numDisplays);
2207 if(err != CGDisplayNoErr)
2209 nlwarning("Cannot get displays (%d)", err);
2210 return false;
2213 // nldebug("3D: %d displays found", (int)numDisplays);
2215 for (CGDisplayCount i = 0; i < numDisplays; ++i)
2217 CGDirectDisplayID dspy = display[i];
2219 #ifdef NL_MAC_VERSION_10_6_UP
2220 CFArrayRef modeList = CGDisplayCopyAllDisplayModes(dspy, NULL);
2221 #else
2222 CFArrayRef modeList = CGDisplayAvailableModes(dspy);
2223 #endif
2225 if (modeList == NULL)
2227 nlwarning("Display is invalid");
2228 continue;
2231 for (CFIndex j = 0; j < CFArrayGetCount(modeList); ++j)
2233 #ifdef NL_MAC_VERSION_10_6_UP
2234 CGDisplayModeRef mode = (CGDisplayModeRef)CFArrayGetValueAtIndex(modeList, j);
2235 uint8 bpp = bppFromDisplayMode(mode);
2236 #else
2237 CFDictionaryRef mode = (CFDictionaryRef)CFArrayGetValueAtIndex(modeList, j);
2238 uint8 bpp = (uint8)GetModeBitsPerPixel(mode);
2239 #endif
2241 if (bpp >= 16)
2243 #ifdef NL_MAC_VERSION_10_6_UP
2244 uint16 w = CGDisplayModeGetWidth(mode);
2245 uint16 h = CGDisplayModeGetHeight(mode);
2246 #else
2247 uint16 w = (uint16)GetModeWidth(mode);
2248 uint16 h = (uint16)GetModeHeight(mode);
2249 #endif
2251 // Add this mode
2252 GfxMode mode;
2253 mode.Width = w;
2254 mode.Height = h;
2255 mode.Depth = bpp;
2257 // Frequency stays at 0 because on mac cocoa, display resolution
2258 // is never really changed. if rendering res < display res,
2259 // cocoa interpolates and keeps the display at it's original res.
2260 mode.Frequency = 0;
2261 modes.push_back (mode);
2263 // nldebug(" Display 0x%x: Mode %dx%d, %d BPP", dspy, w, h, bpp);
2267 #ifdef NL_MAC_VERSION_10_6_UP
2268 CFRelease(modeList);
2269 #endif
2272 #elif defined (NL_OS_UNIX)
2274 if (!_dpy)
2276 nlwarning("3D: Unable to list modes because Display is NULL, did you forget to call init() ?");
2277 return false;
2280 bool found = false;
2281 int screen = DefaultScreen(_dpy);
2283 #if defined(HAVE_XRANDR)
2284 if (_xrandr_version >= 105)
2286 int nmonitors = 0;
2287 // virtual monitors
2288 XRRMonitorInfo *monitor = XRRGetMonitors(_dpy, RootWindow(_dpy, screen), 1, &nmonitors);
2289 for(sint i = 0; i < nmonitors; ++i)
2291 char * name = XGetAtomName(_dpy, monitor[i].name);
2292 GfxMode mode;
2293 mode.DisplayDevice = name;
2294 mode.Width = monitor[i].width;
2295 mode.Height = monitor[i].height;
2296 mode.Frequency = 0;
2297 modes.push_back(mode);
2298 XFree(name);
2300 XRRFreeMonitors(monitor);
2302 else
2304 XRRScreenResources *resources = XRRGetScreenResourcesCurrent(_dpy, RootWindow(_dpy, screen));
2305 if (!resources)
2306 resources = XRRGetScreenResources(_dpy, RootWindow(_dpy, screen));
2308 std::map<int, int> resourceModeMap;
2309 for(sint i = 0; i< resources->nmode; ++i)
2310 resourceModeMap.insert(std::make_pair(resources->modes[i].id, i));
2312 for(sint i = 0; i < resources->noutput; ++i)
2314 XRROutputInfo *output = XRRGetOutputInfo(_dpy, resources, resources->outputs[i]);
2315 if (!output)
2316 continue;
2318 if (output->crtc && output->connection == RR_Connected)
2320 // physical monitor
2321 XRRCrtcInfo *crtc = XRRGetCrtcInfo(_dpy, resources, output->crtc);
2322 if (crtc)
2324 std::map<int,int>::const_iterator it = resourceModeMap.find(crtc->mode);
2325 if (it != resourceModeMap.end())
2327 GfxMode mode;
2328 mode.DisplayDevice = output->name;
2329 mode.Width = resources->modes[it->second].width;
2330 mode.Height = resources->modes[it->second].height;
2331 mode.Frequency = 0;
2333 modes.push_back(mode);
2335 XRRFreeCrtcInfo(crtc);
2338 XRRFreeOutputInfo(output);
2341 XRRFreeScreenResources(resources);
2343 found = modes.size() > 0;
2344 if (!found && _xrandr_version >= 100)
2346 XRRScreenConfiguration *screen_config = XRRGetScreenInfo(_dpy, RootWindow(_dpy, screen));
2348 if (screen_config)
2350 // retrieve the list of resolutions
2351 int nsizes = 0;
2352 XRRScreenSize *sizes = XRRConfigSizes(screen_config, &nsizes);
2354 if (nsizes > 0)
2356 // nldebug("3D: %d available XRandR modes:", nsizes);
2357 for (sint i = 0; i < nsizes; ++i)
2359 // Add this mode
2360 GfxMode mode;
2361 mode.Width = sizes[i].width;
2362 mode.Height = sizes[i].height;
2363 mode.Depth = 32;
2364 mode.Frequency = 0;
2365 modes.push_back(mode);
2366 // nldebug("3D: Mode %d: %dx%d", i, mode.Width, mode.Height);
2369 found = true;
2371 else
2373 nlwarning("3D: No XRandR modes available");
2376 XRRFreeScreenConfigInfo(screen_config);
2378 else
2380 nlwarning("3D: XRRGetScreenInfo failed");
2383 #endif
2385 #ifdef XF86VIDMODE
2386 if (!found && _xvidmode_version > 0)
2388 int nmodes;
2389 XF86VidModeModeInfo **ms;
2390 if (XF86VidModeGetAllModeLines(_dpy, screen, &nmodes, &ms))
2392 // nlinfo("3D: %d available XF86VidMode modes:", nmodes);
2393 for (int j = 0; j < nmodes; j++)
2395 // Add this mode
2396 GfxMode mode;
2397 mode.Width = (uint16)ms[j]->hdisplay;
2398 mode.Height = (uint16)ms[j]->vdisplay;
2399 mode.Frequency = modeInfoToFrequency(ms[j]);
2400 // nlinfo("3D: Mode %d: %dx%d, %d Hz", j, mode.Width, mode.Height, mode.Frequency);
2401 modes.push_back (mode);
2403 XFree(ms);
2405 else
2407 nlwarning("3D: XF86VidModeGetAllModeLines failed");
2410 #endif // XF86VIDMODE
2412 if (!found)
2414 // Add current screen mode
2415 GfxMode mode;
2416 mode.Width = DisplayWidth(_dpy, screen);
2417 mode.Height = DisplayHeight(_dpy, screen);
2418 mode.Frequency = 0;
2419 modes.push_back(mode);
2422 #endif
2423 return true;
2426 // --------------------------------------------------
2427 bool CDriverGL::getCurrentScreenMode(GfxMode &mode)
2429 H_AUTO_OGL(CDriverGL_getCurrentScreenMode)
2431 #ifdef NL_OS_WINDOWS
2433 DEVMODE devmode;
2434 devmode.dmSize = sizeof(DEVMODE);
2435 devmode.dmDriverExtra = 0;
2436 EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &devmode);
2438 mode.Windowed = _CurrentMode.Windowed;
2439 mode.OffScreen = false;
2440 mode.Depth = (uint8)devmode.dmBitsPerPel;
2441 mode.Frequency = devmode.dmDisplayFrequency;
2442 mode.Width = (uint16)devmode.dmPelsWidth;
2443 mode.Height = (uint16)devmode.dmPelsHeight;
2444 mode.AntiAlias = _CurrentMode.AntiAlias;
2446 #elif defined(NL_OS_MAC)
2448 // the sceen with the menu bar
2449 NSScreen* screen = [[NSScreen screens] objectAtIndex:0];
2451 mode.OffScreen = false;
2452 mode.Frequency = 0;
2453 mode.Depth = NSBitsPerPixelFromDepth([screen depth]);
2455 // in fullscreen mode
2456 if([containerView() isInFullScreenMode])
2458 // return the size of the back buffer (like having switched monitor mode)
2459 mode.Windowed = false;
2460 mode.Width = _backBufferWidth;
2461 mode.Height = _backBufferHeight;
2464 // in windowed mode
2465 else
2467 // return the size of the screen with menu bar
2468 mode.Windowed = true;
2469 mode.Width = (uint16)[screen frame].size.width;
2470 mode.Height = (uint16)[screen frame].size.height;
2473 #elif defined(NL_OS_UNIX)
2475 bool found = false;
2476 int screen = DefaultScreen(_dpy);
2478 #ifdef HAVE_XRANDR
2479 int x = 0;
2480 int y = 0;
2481 Window child;
2483 // get window position so we can compare monitors (or mouse position if window not visible yet)
2484 XWindowAttributes xwa;
2485 XGetWindowAttributes(_dpy, _win, &xwa);
2486 if (xwa.map_state != IsUnmapped)
2488 XTranslateCoordinates(_dpy, _win, xwa.root, xwa.x, xwa.y, &x, &y, &child);
2490 else
2492 sint rx, ry, wx, wy;
2493 uint mask;
2494 Bool res = XQueryPointer(_dpy, RootWindow(_dpy, screen), &child, &child, &rx, &ry, &wx, &wy, &mask);
2495 if (res)
2497 x = rx;
2498 y = ry;
2502 if (_xrandr_version >= 105)
2504 int nmonitors = 0;
2505 XRRMonitorInfo *monitor = XRRGetMonitors(_dpy, RootWindow(_dpy, screen), 1, &nmonitors);
2506 if (monitor)
2508 sint bestMatch = -1;
2509 for(sint i = 0; i< nmonitors; ++i)
2511 if ((x >= monitor[i].x && x < (monitor[i].x + monitor[i].width) &&
2512 y >= monitor[i].y && y < (monitor[i].y + monitor[i].height)) ||
2513 (monitor[i].primary && bestMatch == -1))
2515 bestMatch = i;
2519 // best match or primary monitor
2520 if (bestMatch != -1)
2522 found = true;
2523 char* pname = XGetAtomName(_dpy, monitor[bestMatch].name);
2524 mode.DisplayDevice = pname;
2525 mode.Width = monitor[bestMatch].width;
2526 mode.Height = monitor[bestMatch].height;
2527 mode.Windowed = _CurrentMode.Windowed;
2528 mode.OffScreen = false;
2529 mode.Depth = (uint) DefaultDepth(_dpy, screen);
2530 mode.Frequency = 0;
2531 XFree(pname);
2534 XRRFreeMonitors(monitor);
2537 else
2539 XRRScreenResources *resources = XRRGetScreenResourcesCurrent(_dpy, RootWindow(_dpy, screen));
2540 if (!resources)
2541 resources = XRRGetScreenResources(_dpy, RootWindow(_dpy, screen));
2543 for(uint i = 0; i < resources->noutput; ++i)
2545 XRROutputInfo *output = XRRGetOutputInfo(_dpy, resources, resources->outputs[i]);
2546 if (!output)
2547 continue;
2549 if (output->crtc && output->connection == RR_Connected)
2551 XRRCrtcInfo *crtc = XRRGetCrtcInfo(_dpy, resources, output->crtc);
2552 if (crtc)
2554 sint width, height;
2555 bool match = false;
2557 // TODO: test rotation
2558 if (crtc->rotation == RR_Rotate_0 || crtc->rotation == RR_Rotate_180)
2560 width = crtc->width;
2561 height = crtc->height;
2563 else
2565 width = crtc->height;
2566 height = crtc->width;
2569 if (x >= crtc->x && y >= crtc->y && x < (crtc->x + width) && y < (crtc->y + height))
2571 found = true;
2572 mode.DisplayDevice = output->name;
2573 mode.Width = width;
2574 mode.Height = height;
2575 mode.Windowed = _CurrentMode.Windowed;
2576 mode.OffScreen = false;
2577 mode.Depth = (uint) DefaultDepth(_dpy, screen);
2578 mode.Frequency = 0;
2581 XRRFreeCrtcInfo(crtc);
2585 XRRFreeOutputInfo(output);
2587 if (found)
2588 break;
2591 XRRFreeScreenResources(resources);
2594 if (!found && _xrandr_version > 0)
2596 XRRScreenConfiguration *screen_config = XRRGetScreenInfo(_dpy, RootWindow(_dpy, screen));
2598 if (screen_config)
2600 int nsizes;
2601 XRRScreenSize *sizes = XRRConfigSizes(screen_config, &nsizes);
2602 if (nsizes > 0)
2604 Rotation cur_rotation;
2605 SizeID size = XRRConfigCurrentConfiguration(screen_config, &cur_rotation);
2607 mode.Windowed = _CurrentMode.Windowed;
2608 mode.OffScreen = false;
2609 mode.Depth = (uint) DefaultDepth(_dpy, screen);
2610 mode.Frequency = 0;
2611 mode.Width = sizes[size].width;
2612 mode.Height = sizes[size].height;
2614 found = true;
2616 nlinfo("3D: Current XRandR mode %d: %dx%d, %dbit", size, mode.Width, mode.Height, mode.Depth);
2618 else
2620 nlwarning("3D: No XRandR modes available");
2623 XRRFreeScreenConfigInfo(screen_config);
2625 else
2627 nlwarning("3D: XRRGetScreenInfo failed");
2631 #endif // HAVE_XRANDR
2633 #ifdef XF86VIDMODE
2635 if (!found && _xvidmode_version > 0)
2637 sint pixelClock;
2638 XF86VidModeModeLine xmode;
2640 if (XF86VidModeGetModeLine(_dpy, screen, &pixelClock, &xmode))
2642 mode.Windowed = _CurrentMode.Windowed;
2643 mode.OffScreen = false;
2644 mode.Depth = (uint) DefaultDepth(_dpy, screen);
2645 mode.Frequency = 1000 * pixelClock / (xmode.htotal * xmode.vtotal) ;
2646 mode.Width = xmode.hdisplay;
2647 mode.Height = xmode.vdisplay;
2648 nlinfo("3D: Current XF86VidMode mode: %dx%d, %d Hz, %dbit", mode.Width, mode.Height, mode.Frequency, mode.Depth);
2650 found = true;
2652 else
2654 nlwarning("3D: XF86VidModeGetModeLine failed, cannot get current video mode");
2658 #endif // XF86VidMode
2660 if (!found)
2662 mode.Windowed = _CurrentMode.Windowed;
2663 mode.OffScreen = _CurrentMode.OffScreen;
2664 mode.Depth = (uint) DefaultDepth(_dpy, screen);
2665 mode.Frequency = 0;
2666 mode.Width = DisplayWidth(_dpy, screen);
2667 mode.Height = DisplayHeight(_dpy, screen);
2669 found = true;
2671 nldebug("Current mode: %dx%d, %d Hz, %dbit", mode.Width, mode.Height, mode.Frequency, mode.Depth);
2674 #endif // NL_OS_UNIX
2676 return true;
2679 // --------------------------------------------------
2680 void CDriverGL::setWindowTitle(const ucstring &title)
2682 H_AUTO_OGL(CDriverGL_setWindowTitle)
2684 if (_win == EmptyWindow)
2685 return;
2687 #ifdef NL_OS_WINDOWS
2689 if (!SetWindowTextW(_win, (WCHAR*)title.c_str()))
2691 nlwarning("SetWindowText failed: %s", formatErrorMessage(getLastError()).c_str());
2694 #elif defined(NL_OS_MAC)
2696 [[containerView() window] setTitle:
2697 [NSString stringWithUTF8String:title.toUtf8().c_str()]];
2699 #elif defined (NL_OS_UNIX)
2701 #ifdef X_HAVE_UTF8_STRING
2702 // UTF8 properties
2703 Xutf8SetWMProperties (_dpy, _win, (char*)title.toUtf8().c_str(), (char*)title.toUtf8().c_str(), NULL, 0, NULL, NULL, NULL);
2704 #else
2705 // standard properties
2706 XTextProperty text_property;
2707 if (XStringListToTextProperty((char**)&title.toUtf8().c_str(), 1, &text_property) != 0)
2709 XSetWMProperties (_dpy, _win, &text_property, &text_property, NULL, 0, NULL, NULL, NULL);
2711 else
2713 nlwarning("3D: Can't convert title to TextProperty");
2715 #endif
2717 #endif // NL_OS_WINDOWS
2720 // ***************************************************************************
2721 void CDriverGL::setWindowPos(sint32 x, sint32 y)
2723 H_AUTO_OGL(CDriverGL_setWindowPos)
2725 _WindowX = x;
2726 _WindowY = y;
2728 if (_win == EmptyWindow || !_CurrentMode.Windowed)
2729 return;
2731 #ifdef NL_OS_WINDOWS
2733 // save relative cursor
2734 POINT cursorPos;
2735 BOOL cursorPosOk = isSystemCursorInClientArea()
2736 && GetCursorPos(&cursorPos)
2737 && ScreenToClient(_win, &cursorPos);
2739 SetWindowPos(_win, NULL, x, y, 0, 0, /*SWP_NOZORDER | SWP_NOACTIVATE |*/ SWP_NOSIZE);
2741 if (cursorPosOk)
2743 ClientToScreen(_win, &cursorPos);
2744 SetCursorPos(cursorPos.x, cursorPos.y);
2747 #elif defined(NL_OS_MAC)
2748 // get the rect (position, size) of the screen with menu bar
2749 NSRect screenRect = [[[NSScreen screens] objectAtIndex:0] frame];
2751 // get the rect (position, size) of the window
2752 NSRect windowRect = [[containerView() window] frame];
2754 // convert y from NeL coordinates to cocoa coordinates
2755 y = screenRect.size.height - y;
2757 // tell cocoa to move the window
2758 [[containerView() window] setFrameTopLeftPoint:NSMakePoint(x, y)];
2760 #elif defined (NL_OS_UNIX)
2762 if (_CurrentMode.Windowed)
2764 // first time requesting decoration sizes
2765 if (_WindowX && _WindowY && !_DecorationWidth && !_DecorationHeight && _WndActive)
2767 _DecorationWidth = -1;
2768 _DecorationHeight = -1;
2770 XMoveWindow(_dpy, _win, x, y);
2773 #endif // NL_OS_WINDOWS
2776 // ***************************************************************************
2777 void CDriverGL::showWindow(bool show)
2779 H_AUTO_OGL(CDriverGL_showWindow)
2781 // don't change window visibility, if we didn't create the window
2782 if (_win == EmptyWindow || !_DestroyWindow)
2783 return;
2785 _WindowVisible = show;
2787 #ifdef NL_OS_WINDOWS
2789 ShowWindow (_win, show ? SW_SHOW:SW_HIDE);
2791 #elif defined(NL_OS_MAC)
2793 // TODO: Missing Mac Implementation for showWindow
2795 #elif defined (NL_OS_UNIX)
2797 if (show)
2799 XMapRaised(_dpy, _win);
2801 // fix window position if windows manager want to impose them
2802 setWindowPos(_WindowX, _WindowY);
2804 else
2806 XUnmapWindow(_dpy, _win);
2809 #endif // NL_OS_WINDOWS
2812 // --------------------------------------------------
2813 emptyProc CDriverGL::getWindowProc()
2815 H_AUTO_OGL(CDriverGL_getWindowProc)
2817 return (emptyProc)GlWndProc;
2820 // --------------------------------------------------
2821 bool CDriverGL::createContext()
2823 #ifdef USE_OPENGLES
2824 uint samples = 0;
2826 if (_CurrentMode.AntiAlias > -1)
2828 if (_CurrentMode.AntiAlias == 0)
2830 samples = 4;
2832 else
2834 samples = _CurrentMode.AntiAlias;
2838 EGLint attribList[] =
2840 EGL_RED_SIZE, 8,
2841 EGL_GREEN_SIZE, 8,
2842 EGL_BLUE_SIZE, 8,
2843 EGL_ALPHA_SIZE, 8,
2844 EGL_DEPTH_SIZE, 16,
2845 EGL_STENCIL_SIZE, 8,
2846 // EGL_SAMPLE_BUFFERS, _CurrentMode.AntiAlias > -1 ? 1:0,
2847 // EGL_SAMPLES, samples,
2848 EGL_RENDERABLE_TYPE,
2849 EGL_OPENGL_ES_BIT,
2850 EGL_NONE
2853 // Get Display
2854 _EglDisplay = EGL_NO_DISPLAY; // eglGetDisplay(_hDC);
2856 if (_EglDisplay == EGL_NO_DISPLAY)
2858 _EglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
2860 if (_EglDisplay == EGL_NO_DISPLAY)
2862 nlwarning("3D: failed to get display 0x%x", eglGetError());
2863 return false;
2867 // Initialize EGL
2868 EGLint majorVersion;
2869 EGLint minorVersion;
2871 if (!eglInitialize(_EglDisplay, &majorVersion, &minorVersion))
2873 return EGL_FALSE;
2876 const char *extensions = eglQueryString(_EglDisplay, EGL_EXTENSIONS);
2879 // Get configs
2880 EGLint numConfigs;
2882 if (!eglGetConfigs(_EglDisplay, NULL, 0, &numConfigs))
2884 return false;
2887 // Choose config
2888 EGLConfig config = NULL;
2890 if (!eglChooseConfig(_EglDisplay, attribList, &config, 1, &numConfigs))
2892 return false;
2895 // Create a surface
2896 _EglSurface = eglCreateWindowSurface(_EglDisplay, config, (EGLNativeWindowType)_win, NULL);
2898 if (_EglSurface == EGL_NO_SURFACE)
2900 return false;
2903 // Create a GL context
2904 EGLint contextAttribs[] =
2906 EGL_CONTEXT_CLIENT_VERSION, 1,
2907 EGL_NONE
2910 _EglContext = eglCreateContext(_EglDisplay, config, EGL_NO_CONTEXT, contextAttribs);
2912 if (_EglContext == EGL_NO_CONTEXT)
2914 return false;
2917 // Make the context current
2918 if (!eglMakeCurrent(_EglDisplay, _EglSurface, _EglSurface, _EglContext))
2920 return false;
2922 #elif defined(NL_OS_WINDOWS)
2923 _hDC = GetDC(_win);
2924 _CurrentMode.Depth = uint8(GetDeviceCaps(_hDC,BITSPIXEL));
2926 wglMakeCurrent(_hDC,NULL);
2927 // ---
2928 memset(&_pfd,0,sizeof(_pfd));
2929 _pfd.nSize = sizeof(_pfd);
2930 _pfd.nVersion = 1;
2931 _pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
2932 _pfd.iPixelType = PFD_TYPE_RGBA;
2933 _pfd.cColorBits = _CurrentMode.Depth;
2934 // Choose best suited Depth Buffer.
2935 if(_CurrentMode.Depth <= 16)
2937 _pfd.cDepthBits = 16;
2939 else
2941 _pfd.cDepthBits = 24;
2942 _pfd.cAlphaBits = 8;
2943 _pfd.cStencilBits = 8;
2945 _pfd.iLayerType = PFD_MAIN_PLANE;
2946 int pf=ChoosePixelFormat(_hDC,&_pfd);
2947 if (!pf)
2949 return false;
2952 if ( !SetPixelFormat(_hDC,pf,&_pfd) )
2954 return false;
2956 _hRC=wglCreateContext(_hDC);
2958 wglMakeCurrent(_hDC,_hRC);
2959 #endif
2961 return true;
2964 // --------------------------------------------------
2965 bool CDriverGL::activate()
2967 H_AUTO_OGL(CDriverGL_activate);
2969 if (_win == EmptyWindow)
2970 return false;
2972 #ifdef USE_OPENGLES
2974 EGLContext ctx = eglGetCurrentContext();
2976 if (ctx != _EglContext)
2978 // Make the context current
2979 if (!eglMakeCurrent(_EglDisplay, _EglSurface, _EglSurface, _EglContext))
2981 return false;
2985 #elif defined(NL_OS_WINDOWS)
2987 HGLRC hglrc = wglGetCurrentContext();
2989 if (hglrc != _hRC)
2990 wglMakeCurrent(_hDC, _hRC);
2992 #elif defined(NL_OS_MAC)
2994 if([NSOpenGLContext currentContext] != _ctx)
2995 [_ctx makeCurrentContext];
2997 #elif defined (NL_OS_UNIX)
2999 GLXContext nctx = glXGetCurrentContext();
3001 if (nctx != NULL && nctx != _ctx)
3002 glXMakeCurrent(_dpy, _win, _ctx);
3004 #endif // USE_OPENGLES
3006 return true;
3009 // --------------------------------------------------
3010 IDriver::TMessageBoxId CDriverGL::systemMessageBox (const char* message, const char* title, IDriver::TMessageBoxType type, TMessageBoxIcon icon)
3012 H_AUTO_OGL(CDriverGL_systemMessageBox)
3013 #ifdef NL_OS_WINDOWS
3014 switch (::MessageBoxW(NULL, nlUtf8ToWide(message), nlUtf8ToWide(title), ((type == retryCancelType) ? MB_RETRYCANCEL :
3015 (type==yesNoCancelType)?MB_YESNOCANCEL:
3016 (type==okCancelType)?MB_OKCANCEL:
3017 (type==abortRetryIgnoreType)?MB_ABORTRETRYIGNORE:
3018 (type==yesNoType)?MB_YESNO|MB_ICONQUESTION:MB_OK)|
3020 ((icon==handIcon)?MB_ICONHAND:
3021 (icon==questionIcon)?MB_ICONQUESTION:
3022 (icon==exclamationIcon)?MB_ICONEXCLAMATION:
3023 (icon==asteriskIcon)?MB_ICONASTERISK:
3024 (icon==warningIcon)?MB_ICONWARNING:
3025 (icon==errorIcon)?MB_ICONERROR:
3026 (icon==informationIcon)?MB_ICONINFORMATION:
3027 (icon==stopIcon)?MB_ICONSTOP:0)))
3029 case IDOK:
3030 return okId;
3031 case IDCANCEL:
3032 return cancelId;
3033 case IDABORT:
3034 return abortId;
3035 case IDRETRY:
3036 return retryId;
3037 case IDIGNORE:
3038 return ignoreId;
3039 case IDYES:
3040 return yesId;
3041 case IDNO:
3042 return noId;
3044 nlstop;
3045 #else // NL_OS_WINDOWS
3046 // TODO: if user did not launch from console, then program "freezes" without explanation or possibility to continue
3047 //IDriver::systemMessageBox (message, title, type, icon);
3048 // log only
3049 printf("%s:%s\n", title, message);
3050 nlwarning("%s: %s", title, message);
3051 #endif // NL_OS_WINDOWS
3052 return okId;
3055 void CDriverGL::getWindowSize(uint32 &width, uint32 &height)
3057 H_AUTO_OGL(CDriverGL_getWindowSize)
3059 if (_CurrentMode.OffScreen)
3061 #ifdef NL_OS_WINDOWS
3063 #ifndef USE_OPENGLES
3064 if (_PBuffer)
3066 nwglQueryPbufferARB( _PBuffer, WGL_PBUFFER_WIDTH_ARB, (int*)&width );
3067 nwglQueryPbufferARB( _PBuffer, WGL_PBUFFER_HEIGHT_ARB, (int*)&height );
3069 #endif
3071 #endif
3073 else
3075 #ifdef NL_OS_MAC
3076 if([containerView() isInFullScreenMode])
3078 width = _backBufferWidth;
3079 height = _backBufferHeight;
3080 return;
3082 #endif
3083 width = _CurrentMode.Width;
3084 height = _CurrentMode.Height;
3088 void CDriverGL::setWindowSize(uint32 width, uint32 height)
3090 H_AUTO_OGL(CDriverGL_setWindowSize)
3092 if (_win == EmptyWindow)
3093 return;
3095 #if defined(NL_OS_WINDOWS)
3097 // resize the window
3098 RECT rc;
3099 SetRect (&rc, 0, 0, width, height);
3100 AdjustWindowRectEx(&rc, GetWindowStyle(_win), GetMenu(_win) != NULL, GetWindowExStyle(_win));
3101 UINT flags = SWP_NOZORDER | SWP_NOACTIVATE;
3102 // set position to (0, 0) if fullscreen
3103 if (_CurrentMode.Windowed)
3104 flags |= SWP_NOMOVE;
3105 SetWindowPos(_win, NULL, 0, 0, rc.right - rc.left, rc.bottom - rc.top, flags);
3107 // init window width and height
3108 RECT clientRect;
3109 GetClientRect(_win, &clientRect);
3110 _CurrentMode.Width = uint16(clientRect.right-clientRect.left);
3111 _CurrentMode.Height = uint16(clientRect.bottom-clientRect.top);
3112 GetWindowRect(_win, &clientRect);
3113 _WindowX = clientRect.left;
3114 _WindowY = clientRect.top;
3116 #elif defined(NL_OS_MAC)
3118 // for fullscreen mode, adjust the back buffer size to desired resolution
3119 if([containerView() isInFullScreenMode])
3121 // disable and re-enable fullscreen
3122 // fixes #1062 (http://dev.ryzom.com/issues/1062)
3123 setWindowStyle(EWSWindowed);
3124 setWindowStyle(EWSFullscreen);
3126 // set the back buffer manually to match the desired rendering resolution
3127 GLint dim[2] = { (GLint)width, (GLint)height };
3128 CGLError error = CGLSetParameter(
3129 (CGLContextObj)[_ctx CGLContextObj],
3130 kCGLCPSurfaceBackingSize, dim);
3132 if(error != kCGLNoError)
3133 nlerror("cannot set kCGLCPSurfaceBackingSize parameter (%s)",
3134 CGLErrorString(error));
3136 _backBufferHeight = height;
3137 _backBufferWidth = width;
3139 else
3141 // only change the window size if the driver created the window itself
3142 if(_DestroyWindow)
3144 // get the windows current frame
3145 NSRect rect = [[containerView() window] frame];
3147 // convert the desired content size to window size
3148 rect = [[containerView() window] frameRectForContentRect:
3149 NSMakeRect(rect.origin.x, rect.origin.y, width, height)];
3151 // update window dimensions
3152 [[containerView() window] setFrame:rect display:YES];
3156 #elif defined(NL_OS_UNIX)
3158 if (!_Resizable)
3160 // Update WM hints (disallow resizing)
3161 XSizeHints *size_hints = XAllocSizeHints();
3163 size_hints->flags = PMinSize | PMaxSize;
3164 size_hints->min_width = width;
3165 size_hints->min_height = height;
3166 size_hints->max_width = width;
3167 size_hints->max_height = height;
3169 XSetWMNormalHints(_dpy, _win, size_hints);
3170 XFree(size_hints);
3172 else
3174 // XSetWMNormalHints(_dpy, _win, StdHints);
3177 if (width != _CurrentMode.Width || height != _CurrentMode.Height)
3179 // resize the window
3180 XResizeWindow(_dpy, _win, width, height);
3182 _CurrentMode.Width = width;
3183 _CurrentMode.Height = height;
3186 #endif // NL_OS_WINDOWS
3189 void CDriverGL::getWindowPos(sint32 &x, sint32 &y)
3191 H_AUTO_OGL(CDriverGL_getWindowPos)
3193 // Off-screen rendering ?
3194 if (_CurrentMode.OffScreen)
3196 x = y = 0;
3198 else
3200 x = _WindowX;
3201 y = _WindowY;
3205 // --------------------------------------------------
3206 bool CDriverGL::isActive()
3208 H_AUTO_OGL(CDriverGL_isActive)
3210 if (_win == EmptyWindow)
3211 return false;
3213 bool res = true;
3215 #ifdef NL_OS_WINDOWS
3217 res = (IsWindow(_win) != FALSE);
3219 #elif defined(NL_OS_MAC)
3220 // TODO: Missing Mac Implementation for isActive (always true if a window is set)
3221 #elif defined (NL_OS_UNIX)
3223 // check if our window is still active
3224 XWindowAttributes attr;
3225 Status status = XGetWindowAttributes(_dpy, _win, &attr);
3227 res = (status == 1);
3229 #endif // NL_OS_UNIX
3231 return res;
3234 // ***************************************************************************
3235 bool CDriverGL::setMonitorColorProperties (const CMonitorColorProperties &properties)
3237 H_AUTO_OGL(CDriverGL_setMonitorColorProperties )
3239 #ifdef NL_OS_WINDOWS
3241 // Get a DC
3242 HDC dc = CreateDCA ("DISPLAY", NULL, NULL, NULL);
3243 if (dc)
3245 // The ramp
3246 WORD ramp[256*3];
3248 // For each composant
3249 uint c;
3250 for( c=0; c<3; c++ )
3252 uint i;
3253 for( i=0; i<256; i++ )
3255 // Floating value
3256 float value = (float)i / 256;
3258 // Contrast
3259 value = (float) max (0.0f, (value-0.5f) * (float) pow (3.f, properties.Contrast[c]) + 0.5f );
3261 // Gamma
3262 value = (float) pow (value, (properties.Gamma[c]>0) ? 1 - 3 * properties.Gamma[c] / 4 : 1 - properties.Gamma[c] );
3264 // Luminosity
3265 value = value + properties.Luminosity[c] / 2.f;
3266 ramp[i+(c<<8)] = (WORD)min ((int)65535, max (0, (int)(value * 65535)));
3270 // Set the ramp
3271 bool result = SetDeviceGammaRamp (dc, ramp) != FALSE;
3273 // Release the DC
3274 ReleaseDC (NULL, dc);
3276 // Returns result
3277 return result;
3279 else
3281 nlwarning ("(CDriverGL::setMonitorColorProperties): can't create DC");
3284 #elif defined(NL_OS_MAC)
3285 // TODO for Mac: implement CDriverGL::setMonitorColorProperties
3286 nlwarning ("CDriverGL::setMonitorColorProperties not implemented");
3288 #elif defined (NL_OS_UNIX)
3289 // TODO for Linux: implement CDriverGL::setMonitorColorProperties
3290 nlwarning ("CDriverGL::setMonitorColorProperties not implemented");
3292 #endif
3294 return false;
3297 #ifdef NL_OS_MAC
3298 void CDriverGL::setupApplicationMenu()
3300 NSMenu* menu;
3301 NSMenuItem* menuItem;
3302 NSString* title;
3303 NSString* appName;
3305 // get the applications name from it's process info
3306 appName = [[NSProcessInfo processInfo] processName];
3308 // create an empty menu object
3309 menu = [[NSMenu alloc] initWithTitle:@""];
3311 // add the about menu item
3312 title = [@"About " stringByAppendingString:appName];
3313 [menu addItemWithTitle:title
3314 action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""];
3316 // separator
3317 [menu addItem:[NSMenuItem separatorItem]];
3319 // add the hide application menu item
3320 title = [@"Hide " stringByAppendingString:appName];
3321 [menu addItemWithTitle:title
3322 action:@selector(hide:) keyEquivalent:@"h"];
3324 // add the hide others menu item
3325 menuItem = [menu addItemWithTitle:@"Hide Others"
3326 action:@selector(hideOtherApplications:) keyEquivalent:@"h"];
3327 [menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask|NSCommandKeyMask)];
3329 // add the show all menu item
3330 [menu addItemWithTitle:@"Show All"
3331 action:@selector(unhideAllApplications:) keyEquivalent:@""];
3333 // separator
3334 [menu addItem:[NSMenuItem separatorItem]];
3337 TODO on quit send EventDestroyWindowId
3339 // add the quit menu item
3340 title = [@"Quit " stringByAppendingString:appName];
3341 [menu addItemWithTitle:title
3342 action:@selector(terminate:) keyEquivalent:@"q"];
3344 // create an empty menu item and put the new menu into it as a subitem
3345 menuItem = [[NSMenuItem alloc] initWithTitle:@""
3346 action:nil keyEquivalent:@""];
3347 [menuItem setSubmenu:menu];
3349 // create a menu for the application
3350 [NSApp setMainMenu:[[NSMenu alloc] initWithTitle:@""]];
3352 // attach the new menu to the applications menu
3353 [[NSApp mainMenu] addItem:menuItem];
3355 #endif
3357 bool CDriverGL::copyTextToClipboard(const std::string &text)
3359 return _EventEmitter.copyTextToClipboard(text);
3362 bool CDriverGL::pasteTextFromClipboard(std::string &text)
3364 return _EventEmitter.pasteTextFromClipboard(text);
3367 #ifdef NL_OS_WINDOWS
3369 bool CDriverGL::convertBitmapToIcon(const NLMISC::CBitmap &bitmap, HICON &icon, uint iconWidth, uint iconHeight, uint iconDepth, const NLMISC::CRGBA &col, sint hotSpotX, sint hotSpotY, bool cursor)
3371 CBitmap src = bitmap;
3373 // resample bitmap if necessary
3374 if (src.getWidth() != iconWidth || src.getHeight() != iconHeight)
3376 src.resample(iconWidth, iconHeight);
3378 CBitmap colorBm;
3379 colorBm.resize(iconWidth, iconHeight, CBitmap::RGBA);
3380 const CRGBA *srcColorPtr = (CRGBA *) &(src.getPixels()[0]);
3381 const CRGBA *srcColorPtrLast = srcColorPtr + (iconWidth * iconHeight);
3382 CRGBA *destColorPtr = (CRGBA *) &(colorBm.getPixels()[0]);
3383 static uint8 alphaThreshold = 127;
3386 destColorPtr->modulateFromColor(*srcColorPtr, col);
3387 std::swap(destColorPtr->R, destColorPtr->B);
3388 ++ srcColorPtr;
3389 ++ destColorPtr;
3391 while (srcColorPtr != srcColorPtrLast);
3393 HBITMAP colorHbm = NULL;
3394 HBITMAP maskHbm = NULL;
3396 if (iconDepth == 16)
3398 std::vector<uint16> colorBm16(iconWidth * iconHeight);
3399 const CRGBA *src32 = (const CRGBA *) &colorBm.getPixels(0)[0];
3401 for (uint k = 0; k < colorBm16.size(); ++k)
3403 colorBm16[k] = ((uint16)(src32[k].R&0xf8)>>3) | ((uint16)(src32[k].G&0xfc)<<3) | ((uint16)(src32[k].B & 0xf8)<<8);
3406 colorHbm = CreateBitmap(iconWidth, iconHeight, 1, 16, &colorBm16[0]);
3407 std::vector<uint8> bitMask((iconWidth * iconHeight + 7) / 8, 0);
3409 for (uint k = 0;k < colorBm16.size(); ++k)
3411 if (src32[k].A <= alphaThreshold)
3413 bitMask[k / 8] |= (0x80 >> (k & 7));
3417 maskHbm = CreateBitmap(iconWidth, iconHeight, 1, 1, &bitMask[0]);
3419 else
3421 colorHbm = CreateBitmap(iconWidth, iconHeight, 1, 32, &colorBm.getPixels(0)[0]);
3422 maskHbm = CreateBitmap(iconWidth, iconHeight, 1, 32, &colorBm.getPixels(0)[0]);
3425 ICONINFO iconInfo;
3426 iconInfo.fIcon = cursor ? FALSE:TRUE;
3427 iconInfo.xHotspot = (DWORD) hotSpotX;
3428 iconInfo.yHotspot = (DWORD) hotSpotY;
3429 iconInfo.hbmMask = maskHbm;
3430 iconInfo.hbmColor = colorHbm;
3432 if (colorHbm && maskHbm)
3434 icon = CreateIconIndirect(&iconInfo);
3438 if (colorHbm) DeleteObject(colorHbm);
3439 if (maskHbm) DeleteObject(maskHbm);
3441 return true;
3444 #elif defined(NL_OS_MAC)
3446 #elif defined(NL_OS_UNIX)
3448 bool CDriverGL::convertBitmapToIcon(const NLMISC::CBitmap &bitmap, std::vector<long> &icon)
3450 // get bitmap width and height
3451 uint width = bitmap.getWidth();
3452 uint height = bitmap.getHeight();
3454 // icon position for bitmap
3455 uint pos = (uint)icon.size();
3457 // extend icon_data size for bitmap
3458 icon.resize(pos + 2 + width*height);
3460 // set bitmap width and height
3461 icon[pos++] = width;
3462 icon[pos++] = height;
3464 // convert RGBA to ARGB
3465 CObjectVector<uint8> pixels = bitmap.getPixels();
3466 for(uint j = 0; j < pixels.size(); j+=4)
3467 icon[pos++] = pixels[j] << 16 | pixels[j+1] << 8 | pixels[j+2] | pixels[j+3] << 24;
3469 return true;
3472 #endif
3474 #ifdef NL_STATIC
3475 } // NLDRIVERGL/ES
3476 #endif
3478 } // NL3D