sync with linuxport @ 18013
[xbmc:xbmc-antiquated.git] / guilib / GraphicContext.cpp
1 /*
2  *      Copyright (C) 2005-2008 Team XBMC
3  *      http://www.xbmc.org
4  *
5  *  This Program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2, or (at your option)
8  *  any later version.
9  *
10  *  This Program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with XBMC; see the file COPYING.  If not, write to
17  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
18  *  http://www.gnu.org/copyleft/gpl.html
19  *
20  */
21
22 #include "include.h"
23 #include "GraphicContext.h"
24 #include "GUIFontManager.h"
25 #include "GUIMessage.h"
26 #include "IMsgSenderCallback.h"
27 #include "Settings.h"
28 #include "GUISettings.h"
29 #include "XBVideoConfig.h"
30 #include "TextureManager.h"
31 #include "../xbmc/utils/SingleLock.h"
32 #include "../xbmc/Application.h"
33
34 #define D3D_CLEAR_STENCIL 0x0l
35
36 #ifdef HAS_SDL_OPENGL
37 #define GLVALIDATE  { CSingleLock locker(*this); ValidateSurface(); }
38 #endif
39 #include "Surface.h"
40 #include "SkinInfo.h"
41 using namespace Surface;
42 #ifdef HAS_GLX
43 #include <X11/extensions/Xinerama.h>
44 #elif defined (__APPLE__)
45 #include "../xbmc/osx/CocoaUtils.h"
46 #endif
47 #ifdef HAS_XRANDR
48 #include "XRandR.h"
49 #endif
50
51 using namespace std;
52
53 CGraphicContext g_graphicsContext;
54 extern bool g_fullScreen;
55
56 /* quick access to a skin setting, fine unless we starts clearing video settings */
57 static CSettingInt* g_guiSkinzoom = NULL;
58
59 CGraphicContext::CGraphicContext(void)
60 {
61   m_iScreenWidth = 720;
62   m_iScreenHeight = 576;
63 #ifndef HAS_SDL
64   m_pd3dDevice = NULL;
65   m_pd3dParams = NULL;
66   m_stateBlock = 0xffffffff;
67 #endif
68 #ifdef HAS_SDL_OPENGL
69   m_maxTextureSize = 2048;
70 #endif
71   m_dwID = 0;
72   m_strMediaDir = "D:\\media";
73   m_bCalibrating = false;
74   m_Resolution = INVALID;
75   m_pCallback = NULL;
76   m_guiScaleX = m_guiScaleY = 1.0f;
77   m_windowResolution = INVALID;
78   m_bFullScreenRoot = false;
79 }
80
81 CGraphicContext::~CGraphicContext(void)
82 {
83 #ifndef HAS_SDL
84   if (m_stateBlock != 0xffffffff)
85   {
86     Get3DDevice()->DeleteStateBlock(m_stateBlock);
87   }
88 #endif
89
90   while (m_viewStack.size())
91   {
92 #ifndef HAS_SDL
93     D3DVIEWPORT8 *viewport = m_viewStack.top();
94 #elif defined(HAS_SDL_2D)
95     SDL_Rect *viewport = m_viewStack.top();
96 #elif defined(HAS_SDL_OPENGL)
97     GLint* viewport = m_viewStack.top();
98 #else
99     int *viewport;
100 #endif
101     m_viewStack.pop();
102     if (viewport) delete [] viewport;
103   }
104 }
105
106 #ifndef HAS_SDL
107 void CGraphicContext::SetD3DDevice(LPDIRECT3DDEVICE8 p3dDevice)
108 {
109   m_pd3dDevice = p3dDevice;
110 }
111
112 void CGraphicContext::SetD3DParameters(D3DPRESENT_PARAMETERS *p3dParams)
113 {
114   m_pd3dParams = p3dParams;
115 }
116 #endif
117
118 bool CGraphicContext::SendMessage(CGUIMessage& message)
119 {
120   if (!m_pCallback) return false;
121   return m_pCallback->SendMessage(message);
122 }
123
124 void CGraphicContext::setMessageSender(IMsgSenderCallback* pCallback)
125 {
126   m_pCallback = pCallback;
127 }
128
129 DWORD CGraphicContext::GetNewID()
130 {
131   m_dwID++;
132   return m_dwID;
133 }
134
135 void CGraphicContext::SetOrigin(float x, float y)
136 {
137   if (m_origins.size())
138     m_origins.push(CPoint(x,y) + m_origins.top());
139   else
140     m_origins.push(CPoint(x,y));
141   AddTransform(TransformMatrix::CreateTranslation(x, y));
142 }
143
144 void CGraphicContext::RestoreOrigin()
145 {
146   m_origins.pop();
147   RemoveTransform();
148 }
149
150 // add a new clip region, intersecting with the previous clip region.
151 bool CGraphicContext::SetClipRegion(float x, float y, float w, float h)
152 { // transform from our origin
153   CPoint origin;
154   if (m_origins.size())
155     origin = m_origins.top();
156   // ok, now intersect with our old clip region
157   CRect rect(x, y, x + w, y + h);
158   rect += origin;
159   if (m_clipRegions.size())
160   { // intersect with original clip region
161     rect.Intersect(m_clipRegions.top());
162   }
163   if (rect.IsEmpty())
164     return false;
165   m_clipRegions.push(rect);
166
167   // here we could set the hardware clipping, if applicable
168   return true;
169 }
170
171 void CGraphicContext::RestoreClipRegion()
172 {
173   if (m_clipRegions.size())
174     m_clipRegions.pop();
175
176   // here we could reset the hardware clipping, if applicable
177 }
178
179 void CGraphicContext::ClipRect(CRect &vertex, CRect &texture, CRect *texture2)
180 {
181   // this is the software clipping routine.  If the graphics hardware is set to do the clipping
182   // (eg via SetClipPlane in D3D for instance) then this routine is unneeded.
183   if (m_clipRegions.size())
184   {
185     // take a copy of the vertex rectangle and intersect
186     // it with our clip region (moved to the same coordinate system)
187     CRect clipRegion(m_clipRegions.top());
188     if (m_origins.size())
189       clipRegion -= m_origins.top();
190     CRect original(vertex);
191     vertex.Intersect(clipRegion);
192     // and use the original to compute the texture coordinates
193     if (original != vertex)
194     {
195       float scaleX = texture.Width() / original.Width();
196       float scaleY = texture.Height() / original.Height();
197       texture.x1 += (vertex.x1 - original.x1) * scaleX;
198       texture.y1 += (vertex.y1 - original.y1) * scaleY;
199       texture.x2 += (vertex.x2 - original.x2) * scaleX;
200       texture.y2 += (vertex.y2 - original.y2) * scaleY;
201       if (texture2)
202       {
203         scaleX = texture2->Width() / original.Width();
204         scaleY = texture2->Height() / original.Height();
205         texture2->x1 += (vertex.x1 - original.x1) * scaleX;
206         texture2->y1 += (vertex.y1 - original.y1) * scaleY;
207         texture2->x2 += (vertex.x2 - original.x2) * scaleX;
208         texture2->y2 += (vertex.y2 - original.y2) * scaleY;
209       }
210     }
211   }
212 }
213
214 bool CGraphicContext::SetViewPort(float fx, float fy , float fwidth, float fheight, bool intersectPrevious /* = false */)
215 {
216 #ifndef HAS_SDL
217   D3DVIEWPORT8 newviewport;
218   D3DVIEWPORT8 *oldviewport = new D3DVIEWPORT8;
219   Get3DDevice()->GetViewport(oldviewport);
220 #elif defined(HAS_SDL_2D)
221   SDL_Rect newviewport;
222   SDL_Rect *oldviewport = new SDL_Rect;
223   SDL_GetClipRect(m_screenSurface->SDL(), oldviewport);
224 #elif defined(HAS_SDL_OPENGL)
225   GLVALIDATE;
226   GLint newviewport[4];
227   GLint* oldviewport = new GLint[4];
228   glGetIntegerv(GL_SCISSOR_BOX, oldviewport);   
229 #endif
230   
231   // transform coordinates - we may have a rotation which changes the positioning of the
232   // minimal and maximal viewport extents.  We currently go to the maximal extent.
233   float x[4], y[4];
234   x[0] = x[3] = fx;
235   x[1] = x[2] = fx + fwidth;
236   y[0] = y[1] = fy;
237   y[2] = y[3] = fy + fheight;
238   float minX = (float)m_iScreenWidth;
239   float maxX = 0;
240   float minY = (float)m_iScreenHeight;
241   float maxY = 0;
242   for (int i = 0; i < 4; i++)
243   {
244     float z = 0;
245     ScaleFinalCoords(x[i], y[i], z);
246     if (x[i] < minX) minX = x[i];
247     if (x[i] > maxX) maxX = x[i];
248     if (y[i] < minY) minY = y[i];
249     if (y[i] > maxY) maxY = y[i];
250   }
251
252   int newLeft = (int)(minX + 0.5f);
253   int newTop = (int)(minY + 0.5f);
254   int newRight = (int)(maxX + 0.5f);
255   int newBottom = (int)(maxY + 0.5f);
256   if (intersectPrevious)
257   {
258     // do the intersection
259 #ifndef HAS_SDL    
260     int oldLeft = (int)oldviewport->X;
261     int oldTop = (int)oldviewport->Y;
262     int oldRight = (int)oldviewport->X + oldviewport->Width;
263     int oldBottom = (int)oldviewport->Y + oldviewport->Height;
264 #elif defined(HAS_SDL_2D)
265     int oldLeft = (int)oldviewport->x;
266     int oldTop = (int)oldviewport->y;
267     int oldRight = (int)oldviewport->x + oldviewport->w;
268     int oldBottom = (int)oldviewport->y + oldviewport->h;
269 #elif defined(HAS_SDL_OPENGL)
270     int oldLeft = (int)oldviewport[0];
271     int oldBottom = m_iScreenHeight - oldviewport[1];       // opengl uses bottomleft as origin
272     int oldTop = oldBottom - oldviewport[3];
273     int oldRight = (int)oldviewport[0] + oldviewport[2];
274 #endif    
275     if (newLeft >= oldRight || newTop >= oldBottom || newRight <= oldLeft || newBottom <= oldTop)
276     { // empty intersection - return false to indicate no rendering should occur
277 #if defined(HAS_SDL_OPENGL)
278       delete [] oldviewport;
279 #else
280       delete oldviewport;
281 #endif
282       return false;
283     }
284     // ok, they intersect, do the intersection
285     if (newLeft < oldLeft) newLeft = oldLeft;
286     if (newTop < oldTop) newTop = oldTop;
287     if (newRight > oldRight) newRight = oldRight;
288     if (newBottom > oldBottom) newBottom = oldBottom;
289   }
290   // check range against screen size
291   if (newRight <= 0 || newBottom <= 0 ||
292       newTop >= m_iScreenHeight || newLeft >= m_iScreenWidth ||
293       newLeft >= newRight || newTop >= newBottom)
294   { // no intersection with the screen
295
296 #if defined(HAS_SDL_OPENGL)
297    delete [] oldviewport;
298 #else
299    delete oldviewport;
300 #endif
301     return false;
302   }
303   // intersection with the screen
304   if (newLeft < 0) newLeft = 0;
305   if (newTop < 0) newTop = 0;
306   if (newRight > m_iScreenWidth) newRight = m_iScreenWidth;
307   if (newBottom > m_iScreenHeight) newBottom = m_iScreenHeight;
308
309   ASSERT(newLeft < newRight);
310   ASSERT(newTop < newBottom);
311
312 #ifndef HAS_SDL
313   newviewport.MinZ = 0.0f;
314   newviewport.MaxZ = 1.0f;
315   newviewport.X = newLeft;
316   newviewport.Y = newTop;
317   newviewport.Width = newRight - newLeft;
318   newviewport.Height = newBottom - newTop;
319   m_pd3dDevice->SetViewport(&newviewport);
320 #elif defined(HAS_SDL_2D)
321   newviewport.x = newLeft;
322   newviewport.y = newTop;
323   newviewport.w = newRight - newLeft;
324   newviewport.h = newBottom - newTop;
325   SDL_SetClipRect(m_screenSurface->SDL(), &newviewport);
326 #elif defined(HAS_SDL_OPENGL)
327   newviewport[0] = newLeft;
328   newviewport[1] = m_iScreenHeight - newBottom; // opengl uses bottomleft as origin
329   newviewport[2] = newRight - newLeft;
330   newviewport[3] = newBottom - newTop;
331   glScissor(newviewport[0], newviewport[1], newviewport[2], newviewport[3]);
332   glViewport(newviewport[0], newviewport[1], newviewport[2], newviewport[3]);
333   VerifyGLState();
334 #endif
335
336   m_viewStack.push(oldviewport);
337
338   UpdateCameraPosition(m_cameras.top());
339   return true;
340 }
341
342 void CGraphicContext::RestoreViewPort()
343 {
344   if (!m_viewStack.size()) return;
345 #ifndef HAS_SDL
346   D3DVIEWPORT8 *oldviewport = (D3DVIEWPORT8*)m_viewStack.top();
347   Get3DDevice()->SetViewport(oldviewport);
348 #elif defined(HAS_SDL_2D)
349   SDL_Rect *oldviewport = (SDL_Rect*)m_viewStack.top();
350   SDL_SetClipRect(m_screenSurface->SDL(), oldviewport);
351 #elif defined(HAS_SDL_OPENGL)
352   GLVALIDATE;
353   GLint* oldviewport = (GLint*)m_viewStack.top();
354   glScissor(oldviewport[0], oldviewport[1], oldviewport[2], oldviewport[3]);
355   glViewport(oldviewport[0], oldviewport[1], oldviewport[2], oldviewport[3]);
356   VerifyGLState();
357 #endif  
358
359   m_viewStack.pop();
360
361 #if defined(HAS_SDL_OPENGL)
362   delete [] oldviewport;
363 #else
364   delete oldviewport;
365 #endif
366
367   UpdateCameraPosition(m_cameras.top());
368 }
369
370 const RECT& CGraphicContext::GetViewWindow() const
371 {
372   return m_videoRect;
373 }
374 void CGraphicContext::SetViewWindow(float left, float top, float right, float bottom)
375 {
376   if (m_bCalibrating)
377   {
378     SetFullScreenViewWindow(m_Resolution);
379   }
380   else
381   {
382     m_videoRect.left = (long)(ScaleFinalXCoord(left, top) + 0.5f);
383     m_videoRect.top = (long)(ScaleFinalYCoord(left, top) + 0.5f);
384     m_videoRect.right = (long)(ScaleFinalXCoord(right, bottom) + 0.5f);
385     m_videoRect.bottom = (long)(ScaleFinalYCoord(right, bottom) + 0.5f);
386   }
387 }
388
389 void CGraphicContext::ClipToViewWindow()
390 {
391 #ifndef HAS_SDL
392   D3DRECT clip = { m_videoRect.left, m_videoRect.top, m_videoRect.right, m_videoRect.bottom };
393   if (m_videoRect.left < 0) clip.x1 = 0;
394   if (m_videoRect.top < 0) clip.y1 = 0;
395   if (m_videoRect.left > m_iScreenWidth - 1) clip.x1 = m_iScreenWidth - 1;
396   if (m_videoRect.top > m_iScreenHeight - 1) clip.y1 = m_iScreenHeight - 1;
397   if (m_videoRect.right > m_iScreenWidth) clip.x2 = m_iScreenWidth;
398   if (m_videoRect.bottom > m_iScreenHeight) clip.y2 = m_iScreenHeight;
399   if (clip.x2 < clip.x1) clip.x2 = clip.x1 + 1;
400   if (clip.y2 < clip.y1) clip.y2 = clip.y1 + 1;
401 #else
402 #ifdef  __GNUC__
403 // TODO: CGraphicContext::ClipToViewWindow not implemented
404 #endif
405 #endif
406 }
407
408 void CGraphicContext::SetFullScreenViewWindow(RESOLUTION &res)
409 {
410   m_videoRect.left = g_settings.m_ResInfo[res].Overscan.left;
411   m_videoRect.top = g_settings.m_ResInfo[res].Overscan.top;
412   m_videoRect.right = g_settings.m_ResInfo[res].Overscan.right;
413   m_videoRect.bottom = g_settings.m_ResInfo[res].Overscan.bottom;
414 }
415
416 void CGraphicContext::SetFullScreenVideo(bool bOnOff)
417 {
418   Lock();
419   m_bFullScreenVideo = bOnOff;
420   SetFullScreenViewWindow(m_Resolution);
421   Unlock();
422 }
423
424 bool CGraphicContext::IsFullScreenVideo() const
425 {
426   return m_bFullScreenVideo;
427 }
428
429 bool CGraphicContext::IsCalibrating() const
430 {
431   return m_bCalibrating;
432 }
433
434 void CGraphicContext::SetCalibrating(bool bOnOff)
435 {
436   m_bCalibrating = bOnOff;
437 }
438
439 bool CGraphicContext::IsValidResolution(RESOLUTION res)
440 {
441   return g_videoConfig.IsValidResolution(res);
442 }
443
444 void CGraphicContext::GetAllowedResolutions(vector<RESOLUTION> &res, bool bAllowPAL60)
445 {
446   bool bCanDoWidescreen = g_videoConfig.HasWidescreen();
447   res.clear();  
448   if (g_videoConfig.HasPAL())
449   {
450     res.push_back(PAL_4x3);
451     if (bCanDoWidescreen) res.push_back(PAL_16x9);
452     if (bAllowPAL60 && g_videoConfig.HasPAL60())
453     {
454       res.push_back(PAL60_4x3);
455       if (bCanDoWidescreen) res.push_back(PAL60_16x9);
456     }
457   }
458   if (g_videoConfig.HasNTSC())
459   {
460     res.push_back(NTSC_4x3);
461     if (bCanDoWidescreen) res.push_back(NTSC_16x9);
462     if (g_videoConfig.Has480p())
463     {
464       res.push_back(HDTV_480p_4x3);
465       if (bCanDoWidescreen) res.push_back(HDTV_480p_16x9);
466     }
467     if (g_videoConfig.Has720p())
468       res.push_back(HDTV_720p);
469     if (g_videoConfig.Has1080i())
470       res.push_back(HDTV_1080i);
471   }
472 #ifdef HAS_SDL
473   if (g_videoConfig.GetNumberOfResolutions())
474   {
475     res.push_back(CUSTOM);
476   }
477   res.push_back(DESKTOP);
478 #endif
479 }
480
481 #ifndef HAS_SDL
482 void CGraphicContext::SetVideoResolution(RESOLUTION &res, BOOL NeedZ, bool forceClear /* = false */)
483 {
484   if (res == AUTORES)
485   {
486     res = g_videoConfig.GetBestMode();
487   }
488   if (!IsValidResolution(res))
489   { // Choose a failsafe resolution that we can actually display
490     CLog::Log(LOGERROR, "The screen resolution requested is not valid, resetting to a valid mode");
491     res = g_videoConfig.GetSafeMode();
492   }
493   
494   if (!m_pd3dParams)
495   {
496     m_Resolution = res;
497     return ;
498   }
499   bool NeedReset = false;
500
501   UINT interval = D3DPRESENT_INTERVAL_ONE;  
502   //if( m_bFullScreenVideo )
503   //  interval = D3DPRESENT_INTERVAL_IMMEDIATE;
504
505 #ifdef PROFILE
506   interval = D3DPRESENT_INTERVAL_IMMEDIATE;
507 #endif
508
509   interval = 0;
510
511   if (interval != m_pd3dParams->FullScreen_PresentationInterval)
512   {
513     m_pd3dParams->FullScreen_PresentationInterval = interval;
514     NeedReset = true;
515   }
516
517
518   if (NeedZ != m_pd3dParams->EnableAutoDepthStencil)
519   {
520     m_pd3dParams->EnableAutoDepthStencil = NeedZ;
521     NeedReset = true;
522   }
523   if (m_Resolution != res)
524   {
525     NeedReset = true;
526     m_pd3dParams->BackBufferWidth = g_settings.m_ResInfo[res].iWidth;
527     m_pd3dParams->BackBufferHeight = g_settings.m_ResInfo[res].iHeight;
528     m_pd3dParams->Flags = g_settings.m_ResInfo[res].dwFlags;
529     m_pd3dParams->Flags |= D3DPRESENTFLAG_LOCKABLE_BACKBUFFER;
530
531     if (res == HDTV_1080i || res == HDTV_720p || m_bFullScreenVideo)
532       m_pd3dParams->BackBufferCount = 1;
533     else
534       m_pd3dParams->BackBufferCount = 2;
535
536     if (res == PAL60_4x3 || res == PAL60_16x9)
537     {
538       if (m_pd3dParams->BackBufferWidth <= 720 && m_pd3dParams->BackBufferHeight <= 480)
539       {
540         m_pd3dParams->FullScreen_RefreshRateInHz = 60;
541       }
542       else
543       {
544         m_pd3dParams->FullScreen_RefreshRateInHz = 0;
545       }
546     }
547     else
548     {
549       m_pd3dParams->FullScreen_RefreshRateInHz = 0;
550     }
551   }
552   Lock();
553   if (m_pd3dDevice)
554   {
555     if (NeedReset)
556     {
557       CLog::Log(LOGDEBUG, "Setting resolution %i", res);
558       m_pd3dDevice->Reset(m_pd3dParams);
559     }
560
561     /* need to clear and preset, otherwise flicker filters won't take effect */
562     if (NeedReset || forceClear)
563     {
564       m_pd3dDevice->Clear( 0L, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER | D3D_CLEAR_STENCIL, 0x00010001, 1.0f, 0L );
565       m_pd3dDevice->Present( NULL, NULL, NULL, NULL );
566     }
567
568     m_iScreenWidth = m_pd3dParams->BackBufferWidth;
569     m_iScreenHeight = m_pd3dParams->BackBufferHeight;
570     m_bWidescreen = (m_pd3dParams->Flags & D3DPRESENTFLAG_WIDESCREEN) != 0;
571   }
572   if ((g_settings.m_ResInfo[m_Resolution].iWidth != g_settings.m_ResInfo[res].iWidth) || (g_settings.m_ResInfo[m_Resolution].iHeight != g_settings.m_ResInfo[res].iHeight))
573   { // set the mouse resolution
574     g_Mouse.SetResolution(g_settings.m_ResInfo[res].iWidth, g_settings.m_ResInfo[res].iHeight, 1, 1);
575     ResetOverscan(g_settings.m_ResInfo[res]);
576   }
577
578   SetFullScreenViewWindow(res);
579   
580   m_Resolution = res;
581   if(NeedReset)
582   {
583     CLog::Log(LOGDEBUG, "We set resolution %i", m_Resolution);
584     if (m_Resolution != INVALID)
585       g_fontManager.ReloadTTFFonts();
586   }
587
588   Unlock();  
589 }
590 // SDL (Linux, Apple, Windows)
591 #else
592 void CGraphicContext::SetVideoResolution(RESOLUTION &res, BOOL NeedZ, bool forceClear /* = false */)
593 {
594   RESOLUTION lastRes = m_Resolution;    
595   if (res == AUTORES)
596   {
597     res = g_videoConfig.GetBestMode();
598   }
599   
600   if (!IsValidResolution(res))
601   { 
602     // Choose a failsafe resolution that we can actually display
603     CLog::Log(LOGERROR, "The screen resolution requested is not valid, resetting to a valid mode");
604     res = g_videoConfig.GetSafeMode();
605   }
606 #ifndef _WIN32PC
607   // FIXME: see if #5256 works also for Linux and Mac
608   if (res>=DESKTOP || g_advancedSettings.m_startFullScreen)
609   {
610     g_advancedSettings.m_fullScreen = true;
611     m_bFullScreenRoot = true;
612     if (res!=m_Resolution)
613     {
614       if (m_Resolution != INVALID)
615         g_settings.m_ResInfo[WINDOW] = g_settings.m_ResInfo[m_Resolution];
616     }
617   }
618   else
619   {
620     g_advancedSettings.m_fullScreen = false;
621     m_bFullScreenRoot = false;
622 #ifdef HAS_XRANDR
623     g_xrandr.RestoreState();
624 #endif
625   }
626   
627   if (res==WINDOW)
628   {
629     g_advancedSettings.m_fullScreen = false;
630     m_bFullScreenRoot = false;
631   }
632
633   if (res==WINDOW || (m_Resolution != res))
634 #else
635   if ((m_Resolution != res) || (m_bFullScreenRoot != g_advancedSettings.m_fullScreen))
636 #endif
637   {
638 #if defined(__APPLE__)
639     // In going FullScreen, m_Resolution == DESKTOP but if using multiple displays
640     // the display resolution will be wrong if the windowed display is moved to
641     // a display with a different resolution. So we have to resort to    
642     // Hack, hack, hack. The basic problem is the resolution is not linked to the 
643     // display so we have to find which display we are going fs on, then search
644     // through the m_ResInfo resolutions to find a matching "Full Screen"
645     // descriptor, then use that index to setup m_Resolution as there are multiple
646     // "DESKTOP" with multiple displays. If the strMode descriptor changes, this
647     // will break but the resolution really need to be linked to a display index.
648     if (m_bFullScreenRoot)
649     {
650       // going to fullscreen desktop but which display if multiple displays?
651       // need to find the m_ResInfo index for that display.
652       int screen_index = Cocoa_GetScreenIndex();
653       char        test_string[256];
654
655       if (screen_index == 1)
656       {
657         strcpy(test_string, "(Full Screen)");
658       }
659       else
660       {
661         sprintf(test_string, "(Full Screen #%d)", screen_index);
662       }
663       for (int i = (int)DESKTOP ; i< (CUSTOM+g_videoConfig.GetNumberOfResolutions()) ; i++)
664       {
665         if (strstr(g_settings.m_ResInfo[i].strMode, test_string) != 0)
666         {
667             res = (RESOLUTION)i;
668             break;
669         }
670       }
671     }
672 #endif
673     Lock();
674     m_iScreenWidth  = g_settings.m_ResInfo[res].iWidth;
675     m_iScreenHeight = g_settings.m_ResInfo[res].iHeight;
676     m_Resolution    = res;
677
678 #ifdef HAS_SDL_2D
679     int options = SDL_HWSURFACE | SDL_DOUBLEBUF;
680     if (g_advancedSettings.m_fullScreen) options |= SDL_FULLSCREEN;
681     m_screenSurface = new CSurface(m_iScreenWidth, m_iScreenHeight, true, 0, 0, 0, (bool)g_advancedSettings.m_fullScreen);
682 #elif defined(HAS_SDL_OPENGL)
683     int options = SDL_RESIZABLE;
684     if (g_advancedSettings.m_fullScreen) options |= SDL_FULLSCREEN;
685
686     // Create a bare root window so that SDL Input handling works
687 #ifdef HAS_GLX
688     static SDL_Surface* rootWindow = NULL;
689     if (!rootWindow) 
690     {
691 #ifdef HAS_XRANDR
692       XOutput out;
693       XMode mode;
694       out.name = g_settings.m_ResInfo[res].strOutput;
695       mode.w = g_settings.m_ResInfo[res].iWidth;
696       mode.h = g_settings.m_ResInfo[res].iHeight;
697       mode.hz = g_settings.m_ResInfo[res].fRefreshRate;
698       g_xrandr.SetMode(out, mode);
699       SDL_ShowCursor(SDL_ENABLE);
700 #endif
701
702       rootWindow = SDL_SetVideoMode(m_iScreenWidth, m_iScreenHeight, 0,  options);
703       // attach a GLX surface to the root window
704       m_screenSurface = new CSurface(m_iScreenWidth, m_iScreenHeight, true, 0, 0, rootWindow, false, false, false, g_advancedSettings.m_fullScreen);
705       if (g_videoConfig.GetVSyncMode()==VSYNC_ALWAYS)
706         m_screenSurface->EnableVSync();
707
708       if (g_advancedSettings.m_fullScreen)
709       {
710         SetFullScreenRoot(true);
711       }
712     } 
713     else 
714     {
715       if (!g_advancedSettings.m_fullScreen)
716       {
717         rootWindow = SDL_SetVideoMode(m_iScreenWidth, m_iScreenHeight, 0,  options);
718         m_screenSurface->ResizeSurface(m_iScreenWidth, m_iScreenHeight);
719       }
720       else
721       {
722         SetFullScreenRoot(true);
723       }
724     }
725
726 #elif defined(__APPLE__) || defined(_WIN32PC)
727     // Allow for fullscreen.
728     bool needsResize = (m_screenSurface != 0);
729 #if defined(_WIN32PC)
730     // Always resize even the first time because we need to change the attributes on the SDL window and center
731     needsResize = true; 
732 #endif
733     if (!m_screenSurface)
734       m_screenSurface = new CSurface(m_iScreenWidth, m_iScreenHeight, true, 0, 0, 0, g_advancedSettings.m_fullScreen);
735
736     if (g_advancedSettings.m_fullScreen)
737     {
738       // SetFullScreenRoot will call m_screenSurface->ResizeSurface
739       needsResize = false;
740       SetFullScreenRoot(true);
741     }
742 #ifndef _WIN32PC
743     else if (lastRes>=DESKTOP )
744 #else
745     else if (m_bFullScreenRoot)
746 #endif
747     {
748       // SetFullScreenRoot will call m_screenSurface->ResizeSurface
749       needsResize = false;
750       SetFullScreenRoot(false);
751     }
752
753     if (needsResize)
754       m_screenSurface->ResizeSurface(m_iScreenWidth, m_iScreenHeight);
755 #endif
756
757 #if defined(_WIN32PC)
758     if (!g_guiSettings.GetBool("videoplayer.adjustrefreshrate"))
759     {
760         //get the display frequency
761         DEVMODE devmode;
762         ZeroMemory(&devmode, sizeof(devmode));
763         devmode.dmSize = sizeof(devmode);
764         EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &devmode);
765         if(devmode.dmDisplayFrequency == 59 || devmode.dmDisplayFrequency == 29 || devmode.dmDisplayFrequency == 23)
766             g_settings.m_ResInfo[res].fRefreshRate = (float)(devmode.dmDisplayFrequency + 1) / 1.001f;
767         else
768             g_settings.m_ResInfo[res].fRefreshRate = (float)(devmode.dmDisplayFrequency);
769     }
770     else
771         if(g_settings.m_ResInfo[res].iSubtitles > g_settings.m_ResInfo[res].iHeight)
772             g_settings.m_ResInfo[res].iSubtitles = (int)(0.965 * g_settings.m_ResInfo[res].iHeight);
773 #endif
774
775     SDL_WM_SetCaption("XBMC Media Center", NULL);
776
777     {
778       CSingleLock aLock(m_surfaceLock);
779       m_surfaces[SDL_ThreadID()] = m_screenSurface;
780     }
781
782     glClearColor( 0.0f, 0.0f, 0.0f, 0.0f );
783
784     {
785       GLint width = 256;
786       glGetError(); // reset any previous GL errors
787
788       // max out at 2^(8+8)
789       for (int i = 0 ; i<8 ; i++) 
790       {
791         glTexImage2D(GL_PROXY_TEXTURE_2D, 0, 4, width, width, 0, GL_BGRA,
792                      GL_UNSIGNED_BYTE, NULL);
793         glGetTexLevelParameteriv(GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_WIDTH,
794                                  &width);
795
796         // GMA950 on OS X sets error instead
797         if (width==0 || (glGetError()!=GL_NO_ERROR) )
798           break;
799         m_maxTextureSize = width;
800         width *= 2;
801         if (width > 65536) // have an upper limit in case driver acts stupid
802         {
803           CLog::Log(LOGERROR, "GL: Could not determine maximum texture width, falling back to 2048");
804           m_maxTextureSize = 2048;
805           break;
806         }
807       }
808       CLog::Log(LOGINFO, "GL: Maximum texture width: %d", m_maxTextureSize);
809     }
810
811     glViewport(0, 0, m_iScreenWidth, m_iScreenHeight);
812     glScissor(0, 0, m_iScreenWidth, m_iScreenHeight);
813
814     glEnable(GL_TEXTURE_2D); 
815     glEnable(GL_SCISSOR_TEST); 
816
817     glMatrixMode(GL_PROJECTION);
818     glLoadIdentity();
819  
820     glOrtho(0.0f, m_iScreenWidth-1, m_iScreenHeight-1, 0.0f, -1.0f, 1.0f);
821
822     glMatrixMode(GL_MODELVIEW);
823     glLoadIdentity(); 
824     
825     glBlendFunc(GL_SRC_ALPHA, GL_ONE);
826     glEnable(GL_BLEND);          // Turn Blending On
827     glDisable(GL_DEPTH_TEST);
828     VerifyGLState();
829 #endif
830     m_bWidescreen = (res == HDTV_1080i || res == HDTV_720p || res == PAL60_16x9 || 
831                         res == PAL_16x9 || res == NTSC_16x9);
832     
833     // set the mouse resolution
834     if ((lastRes == -1) || (g_settings.m_ResInfo[lastRes].iWidth != g_settings.m_ResInfo[res].iWidth) || (g_settings.m_ResInfo[lastRes].iHeight != g_settings.m_ResInfo[res].iHeight))
835     {
836       g_Mouse.SetResolution(g_settings.m_ResInfo[res].iWidth, g_settings.m_ResInfo[res].iHeight, 1, 1);
837       g_fontManager.ReloadTTFFonts();
838     }
839    
840     // restore vsync mode
841     g_videoConfig.SetVSyncMode((VSYNC)g_guiSettings.GetInt("videoscreen.vsync"));
842
843     SetFullScreenViewWindow(res);
844
845     m_Resolution = res;
846     Unlock();  
847   }
848 }
849
850 #endif
851
852
853 RESOLUTION CGraphicContext::GetVideoResolution() const
854 {
855   return m_Resolution;
856 }
857
858 void CGraphicContext::ResetOverscan(RESOLUTION_INFO &res)
859 {
860   res.Overscan.left = 0;
861   res.Overscan.top = 0;
862   res.Overscan.right = res.iWidth;
863   res.Overscan.bottom = res.iHeight;
864 }
865
866 void CGraphicContext::ResetOverscan(RESOLUTION res, OVERSCAN &overscan)
867 {
868   overscan.left = 0;
869   overscan.top = 0;
870   switch (res)
871   {
872   case HDTV_1080i:
873     overscan.right = 1920;
874     overscan.bottom = 1080;
875     break;
876   case HDTV_720p:
877     overscan.right = 1280;
878     overscan.bottom = 720;
879     break;
880   case HDTV_480p_16x9:
881   case HDTV_480p_4x3:
882   case NTSC_16x9:
883   case NTSC_4x3:
884   case PAL60_16x9:
885   case PAL60_4x3:
886     overscan.right = 720;
887     overscan.bottom = 480;
888     break;
889   case PAL_16x9:
890   case PAL_4x3:
891     overscan.right = 720;
892     overscan.bottom = 576;
893     break;
894   default:
895     overscan.right = g_settings.m_ResInfo[res].iWidth;
896     overscan.bottom = g_settings.m_ResInfo[res].iHeight;
897     break;
898   }
899 }
900
901 void CGraphicContext::ResetScreenParameters(RESOLUTION res)
902 {
903   // For now these are all on the first screen.
904   g_settings.m_ResInfo[res].iScreen = 0;
905   static const float fOptimalSwitchPoint = 8.0f / (3.0f*sqrt(3.0f)); // see XboxRenderer.cpp
906   
907   // 1080i
908   switch (res)
909   {
910   case HDTV_1080i:
911     g_settings.m_ResInfo[res].iSubtitles = (int)(0.965 * 1080);
912     g_settings.m_ResInfo[res].iWidth = 1920;
913     g_settings.m_ResInfo[res].iHeight = 1080;
914     g_settings.m_ResInfo[res].dwFlags = D3DPRESENTFLAG_INTERLACED | D3DPRESENTFLAG_WIDESCREEN;
915     g_settings.m_ResInfo[res].fPixelRatio = 1.0f;
916     strcpy(g_settings.m_ResInfo[res].strMode, "1080i 16:9");
917     break;
918   case HDTV_720p:
919     g_settings.m_ResInfo[res].iSubtitles = (int)(0.965 * 720);
920     g_settings.m_ResInfo[res].iWidth = 1280;
921     g_settings.m_ResInfo[res].iHeight = 720;
922     g_settings.m_ResInfo[res].dwFlags = D3DPRESENTFLAG_PROGRESSIVE | D3DPRESENTFLAG_WIDESCREEN;
923     g_settings.m_ResInfo[res].fPixelRatio = 1.0f;
924     strcpy(g_settings.m_ResInfo[res].strMode, "720p 16:9");
925     break;
926   case HDTV_480p_4x3:
927     g_settings.m_ResInfo[res].iSubtitles = (int)(0.9 * 480);
928     g_settings.m_ResInfo[res].iWidth = 720;
929     g_settings.m_ResInfo[res].iHeight = 480;
930     g_settings.m_ResInfo[res].dwFlags = D3DPRESENTFLAG_PROGRESSIVE;
931     g_settings.m_ResInfo[res].fPixelRatio = 4320.0f / 4739.0f;
932     strcpy(g_settings.m_ResInfo[res].strMode, "480p 4:3");
933     break;
934   case HDTV_480p_16x9:
935     g_settings.m_ResInfo[res].iSubtitles = (int)(0.965 * 480);
936     g_settings.m_ResInfo[res].iWidth = 720;
937     g_settings.m_ResInfo[res].iHeight = 480;
938     g_settings.m_ResInfo[res].dwFlags = D3DPRESENTFLAG_PROGRESSIVE | D3DPRESENTFLAG_WIDESCREEN;
939     g_settings.m_ResInfo[res].fPixelRatio = 4320.0f / 4739.0f*4.0f / 3.0f;
940     strcpy(g_settings.m_ResInfo[res].strMode, "480p 16:9");
941     break;
942   case NTSC_4x3:
943     g_settings.m_ResInfo[res].iSubtitles = (int)(0.9 * 480);
944     g_settings.m_ResInfo[res].iWidth = 720;
945     g_settings.m_ResInfo[res].iHeight = 480;
946     g_settings.m_ResInfo[res].dwFlags = D3DPRESENTFLAG_INTERLACED;
947     g_settings.m_ResInfo[res].fPixelRatio = 4320.0f / 4739.0f;
948     strcpy(g_settings.m_ResInfo[res].strMode, "NTSC 4:3");
949     break;
950   case NTSC_16x9:
951     g_settings.m_ResInfo[res].iSubtitles = (int)(0.965 * 480);
952     g_settings.m_ResInfo[res].iWidth = 720;
953     g_settings.m_ResInfo[res].iHeight = 480;
954     g_settings.m_ResInfo[res].dwFlags = D3DPRESENTFLAG_INTERLACED | D3DPRESENTFLAG_WIDESCREEN;
955     g_settings.m_ResInfo[res].fPixelRatio = 4320.0f / 4739.0f*4.0f / 3.0f;
956     strcpy(g_settings.m_ResInfo[res].strMode, "NTSC 16:9");
957     break;
958   case PAL_4x3:
959     g_settings.m_ResInfo[res].iSubtitles = (int)(0.9 * 576);
960     g_settings.m_ResInfo[res].iWidth = 720;
961     g_settings.m_ResInfo[res].iHeight = 576;
962     g_settings.m_ResInfo[res].dwFlags = D3DPRESENTFLAG_INTERLACED;
963     g_settings.m_ResInfo[res].fPixelRatio = 128.0f / 117.0f;
964     strcpy(g_settings.m_ResInfo[res].strMode, "PAL 4:3");
965     break;
966   case PAL_16x9:
967     g_settings.m_ResInfo[res].iSubtitles = (int)(0.965 * 576);
968     g_settings.m_ResInfo[res].iWidth = 720;
969     g_settings.m_ResInfo[res].iHeight = 576;
970     g_settings.m_ResInfo[res].dwFlags = D3DPRESENTFLAG_INTERLACED | D3DPRESENTFLAG_WIDESCREEN;
971     g_settings.m_ResInfo[res].fPixelRatio = 128.0f / 117.0f*4.0f / 3.0f;
972     strcpy(g_settings.m_ResInfo[res].strMode, "PAL 16:9");
973     break;
974   case PAL60_4x3:
975     g_settings.m_ResInfo[res].iSubtitles = (int)(0.9 * 480);
976     g_settings.m_ResInfo[res].iWidth = 720;
977     g_settings.m_ResInfo[res].iHeight = 480;
978     g_settings.m_ResInfo[res].dwFlags = D3DPRESENTFLAG_INTERLACED;
979     g_settings.m_ResInfo[res].fPixelRatio = 4320.0f / 4739.0f;
980     strcpy(g_settings.m_ResInfo[res].strMode, "PAL60 4:3");
981     break;
982   case PAL60_16x9:
983     g_settings.m_ResInfo[res].iSubtitles = (int)(0.965 * 480);
984     g_settings.m_ResInfo[res].iWidth = 720;
985     g_settings.m_ResInfo[res].iHeight = 480;
986     g_settings.m_ResInfo[res].dwFlags = D3DPRESENTFLAG_INTERLACED | D3DPRESENTFLAG_WIDESCREEN;
987     g_settings.m_ResInfo[res].fPixelRatio = 4320.0f / 4739.0f*4.0f / 3.0f;
988     strcpy(g_settings.m_ResInfo[res].strMode, "PAL60 16:9");
989     break;
990   case DESKTOP:
991     g_videoConfig.GetCurrentResolution(g_settings.m_ResInfo[res]);
992     g_settings.m_ResInfo[res].iSubtitles = (int)(0.965 * g_settings.m_ResInfo[res].iHeight);
993     if(g_settings.m_ResInfo[res].fRefreshRate)
994       snprintf(g_settings.m_ResInfo[res].strMode
995             , sizeof(g_settings.m_ResInfo[res].strMode)
996             , "%dx%d @ %.2fHz (Full Screen)"
997             , g_settings.m_ResInfo[res].iWidth
998             , g_settings.m_ResInfo[res].iHeight
999             , g_settings.m_ResInfo[res].fRefreshRate);
1000     else
1001       snprintf(g_settings.m_ResInfo[res].strMode
1002             , sizeof(g_settings.m_ResInfo[res].strMode)
1003             , "%dx%d (Full Screen)"
1004             , g_settings.m_ResInfo[res].iWidth
1005             , g_settings.m_ResInfo[res].iHeight);
1006     if ((float)g_settings.m_ResInfo[res].iWidth/(float)g_settings.m_ResInfo[res].iHeight >= fOptimalSwitchPoint)
1007       g_settings.m_ResInfo[res].dwFlags = D3DPRESENTFLAG_WIDESCREEN;
1008     g_settings.m_ResInfo[res].fPixelRatio = 1.0f;
1009     break;
1010   case WINDOW:
1011     {
1012       RESOLUTION_INFO info = {};
1013       g_videoConfig.GetCurrentResolution(info);
1014       g_settings.m_ResInfo[res] = g_settings.m_ResInfo[PAL60_4x3];
1015       g_settings.m_ResInfo[res].fPixelRatio = 1.0f;
1016       g_settings.m_ResInfo[res].fRefreshRate = info.fRefreshRate;
1017     }
1018     break;
1019   default:
1020     break;
1021   }
1022   ResetOverscan(res, g_settings.m_ResInfo[res].Overscan);
1023 }
1024
1025 float CGraphicContext::GetPixelRatio(RESOLUTION iRes) const
1026 {
1027   // TODO: Why do we set a pixel ratio of 1.0 if the
1028   //       user is not running fullscreen?
1029   if (!m_bFullScreenRoot)
1030     return 1.0f;
1031
1032   return g_settings.m_ResInfo[iRes].fPixelRatio;
1033 }
1034
1035 void CGraphicContext::Clear()
1036 {
1037 #ifndef HAS_SDL
1038   if (!m_pd3dDevice) return;
1039   //Not trying to clear the zbuffer when there is none is 7 fps faster (pal resolution)
1040   if ((!m_pd3dParams) || (m_pd3dParams->EnableAutoDepthStencil == TRUE))
1041     m_pd3dDevice->Clear( 0L, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER | D3D_CLEAR_STENCIL, 0x00010001, 1.0f, 0L );
1042   else
1043     m_pd3dDevice->Clear( 0L, NULL, D3DCLEAR_TARGET, 0x00010001, 1.0f, 0L );
1044 #elif defined(HAS_SDL_2D)
1045   SDL_FillRect(m_screenSurface->SDL(), NULL, 0x00010001);
1046 #elif defined(HAS_SDL_OPENGL)
1047   GLVALIDATE;
1048   glClear(GL_COLOR_BUFFER_BIT); 
1049 #endif    
1050 }
1051
1052 void CGraphicContext::CaptureStateBlock()
1053 {
1054 #ifndef HAS_SDL
1055   if (m_stateBlock != 0xffffffff)
1056   {
1057     Get3DDevice()->DeleteStateBlock(m_stateBlock);
1058   }
1059
1060   if (D3D_OK != Get3DDevice()->CreateStateBlock(D3DSBT_PIXELSTATE, &m_stateBlock))
1061   {
1062     // Creation failure
1063     m_stateBlock = 0xffffffff;
1064   }
1065 #endif  
1066 #ifdef HAS_SDL_OPENGL
1067   glMatrixMode(GL_PROJECTION);
1068   glPushMatrix();
1069   glMatrixMode(GL_TEXTURE);
1070   glPushMatrix();
1071   glMatrixMode(GL_MODELVIEW);
1072   glPushMatrix();
1073   glDisable(GL_SCISSOR_TEST); // fixes FBO corruption on Macs
1074   if (glActiveTextureARB)
1075     glActiveTextureARB(GL_TEXTURE0_ARB);
1076   glDisable(GL_TEXTURE_2D);
1077   glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
1078   glColor3f(1.0, 1.0, 1.0);
1079 #endif
1080 }
1081
1082 void CGraphicContext::ApplyStateBlock()
1083 {
1084 #ifndef HAS_SDL
1085   if (m_stateBlock != 0xffffffff)
1086   {
1087     Get3DDevice()->ApplyStateBlock(m_stateBlock);
1088   }
1089 #endif
1090 #ifdef HAS_SDL_OPENGL
1091   glMatrixMode(GL_PROJECTION);
1092   glPopMatrix();
1093   glMatrixMode(GL_TEXTURE);
1094   glPopMatrix();
1095   glMatrixMode(GL_MODELVIEW);
1096   glPopMatrix();
1097   if (glActiveTextureARB)
1098     glActiveTextureARB(GL_TEXTURE0_ARB);
1099   glEnable(GL_TEXTURE_2D);
1100   glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
1101   glEnable(GL_BLEND);
1102   glEnable(GL_SCISSOR_TEST);
1103 #endif
1104 }
1105
1106 void CGraphicContext::SetScalingResolution(RESOLUTION res, float posX, float posY, bool needsScaling)
1107 {
1108   Lock();
1109   m_windowResolution = res;
1110   if (needsScaling && m_Resolution != INVALID)
1111   {
1112     // calculate necessary scalings    
1113     float fFromWidth;
1114     float fFromHeight;
1115     float fToPosX;
1116     float fToPosY;
1117     float fToWidth;
1118     float fToHeight;
1119     
1120     {
1121       fFromWidth = (float)g_settings.m_ResInfo[res].iWidth;
1122       fFromHeight = (float)g_settings.m_ResInfo[res].iHeight;
1123       fToPosX = (float)g_settings.m_ResInfo[m_Resolution].Overscan.left;
1124       fToPosY = (float)g_settings.m_ResInfo[m_Resolution].Overscan.top;
1125       fToWidth = (float)g_settings.m_ResInfo[m_Resolution].Overscan.right - fToPosX;
1126       fToHeight = (float)g_settings.m_ResInfo[m_Resolution].Overscan.bottom - fToPosY;      
1127     }
1128
1129     // add additional zoom to compensate for any overskan built in skin
1130     float fZoom = g_SkinInfo.GetSkinZoom();
1131
1132     if(!g_guiSkinzoom) // lookup gui setting if we didn't have it already
1133       g_guiSkinzoom = (CSettingInt*)g_guiSettings.GetSetting("lookandfeel.skinzoom");
1134
1135     if(g_guiSkinzoom)
1136       fZoom *= (100 + g_guiSkinzoom->GetData()) * 0.01f;
1137
1138     fZoom -= 1.0f;
1139     fToPosX -= fToWidth * fZoom * 0.5f;
1140     fToWidth *= fZoom + 1.0f;
1141
1142     // adjust for aspect ratio as zoom is given in the vertical direction and we don't 
1143     // do aspect ratio corrections in the gui code 
1144     fZoom = fZoom / g_settings.m_ResInfo[m_Resolution].fPixelRatio;
1145     fToPosY -= fToHeight * fZoom * 0.5f;
1146     fToHeight *= fZoom + 1.0f;
1147     
1148     m_guiScaleX = fFromWidth / fToWidth;
1149     m_guiScaleY = fFromHeight / fToHeight;
1150     TransformMatrix windowOffset = TransformMatrix::CreateTranslation(posX, posY);
1151     TransformMatrix guiScaler = TransformMatrix::CreateScaler(fToWidth / fFromWidth, fToHeight / fFromHeight, fToHeight / fFromHeight);
1152     TransformMatrix guiOffset = TransformMatrix::CreateTranslation(fToPosX, fToPosY);
1153     m_guiTransform = guiOffset * guiScaler * windowOffset;
1154   }
1155   else
1156   {
1157     m_guiTransform = TransformMatrix::CreateTranslation(posX, posY);
1158     m_guiScaleX = 1.0f;
1159     m_guiScaleY = 1.0f;
1160   }
1161   // reset our origin and camera
1162   while (m_origins.size())
1163     m_origins.pop();
1164   m_origins.push(CPoint(posX, posY));
1165   while (m_cameras.size())
1166     m_cameras.pop();
1167   m_cameras.push(CPoint(0.5f*m_iScreenWidth, 0.5f*m_iScreenHeight));
1168
1169   // and reset the final transform
1170   UpdateFinalTransform(m_guiTransform);
1171   Unlock();
1172 }
1173
1174 void CGraphicContext::SetRenderingResolution(RESOLUTION res, float posX, float posY, bool needsScaling)
1175 {
1176   Lock();
1177   SetScalingResolution(res, posX, posY, needsScaling);
1178   UpdateCameraPosition(m_cameras.top());
1179   Unlock();
1180 }
1181
1182 void CGraphicContext::UpdateFinalTransform(const TransformMatrix &matrix)
1183 {
1184   m_finalTransform = matrix;
1185   // We could set the world transform here to GPU-ize the animation system.
1186   // trouble is that we require the resulting x,y coords to be rounded to
1187   // the nearest pixel (vertex shader perhaps?)
1188 }
1189
1190 void CGraphicContext::InvertFinalCoords(float &x, float &y) const
1191 {
1192   m_finalTransform.InverseTransformPosition(x, y);
1193 }
1194
1195 float CGraphicContext::GetScalingPixelRatio() const
1196 {
1197   if (m_Resolution == m_windowResolution)
1198     return GetPixelRatio(m_windowResolution);
1199
1200   RESOLUTION checkRes = m_windowResolution;
1201   if (checkRes == INVALID)
1202     checkRes = m_Resolution;
1203   // resolutions are different - we want to return the aspect ratio of the video resolution
1204   // but only once it's been corrected for the skin -> screen coordinates scaling
1205   float winWidth = (float)g_settings.m_ResInfo[checkRes].iWidth;
1206   float winHeight = (float)g_settings.m_ResInfo[checkRes].iHeight;
1207   float outWidth = (float)g_settings.m_ResInfo[m_Resolution].iWidth;
1208   float outHeight = (float)g_settings.m_ResInfo[m_Resolution].iHeight;
1209   float outPR = GetPixelRatio(m_Resolution);
1210
1211   return outPR * (outWidth / outHeight) / (winWidth / winHeight);
1212 }
1213
1214 void CGraphicContext::SetCameraPosition(const CPoint &camera)
1215 {
1216   // offset the camera from our current location (this is in XML coordinates) and scale it up to
1217   // the screen resolution
1218   CPoint cam(camera);
1219   if (m_origins.size())
1220     cam += m_origins.top();
1221
1222   RESOLUTION windowRes = (m_windowResolution == INVALID) ? m_Resolution : m_windowResolution;
1223   cam.x *= (float)m_iScreenWidth / g_settings.m_ResInfo[windowRes].iWidth;
1224   cam.y *= (float)m_iScreenHeight / g_settings.m_ResInfo[windowRes].iHeight;
1225
1226   m_cameras.push(cam);
1227   UpdateCameraPosition(m_cameras.top());
1228 }
1229
1230 void CGraphicContext::RestoreCameraPosition()
1231 { // remove the top camera from the stack
1232   ASSERT(m_cameras.size());
1233   m_cameras.pop();
1234   UpdateCameraPosition(m_cameras.top());
1235 }
1236
1237 void CGraphicContext::UpdateCameraPosition(const CPoint &camera)
1238 {
1239   // NOTE: This routine is currently called (twice) every time there is a <camera>
1240   //       tag in the skin.  It actually only has to be called before we render
1241   //       something, so another option is to just save the camera coordinates
1242   //       and then have a routine called before every draw that checks whether
1243   //       the camera has changed, and if so, changes it.  Similarly, it could set
1244   //       the world transform at that point as well (or even combine world + view
1245   //       to cut down on one setting)
1246  
1247   // and calculate the offset from the screen center
1248   CPoint offset = camera - CPoint(m_iScreenWidth*0.5f, m_iScreenHeight*0.5f);
1249
1250 #if defined(HAS_SDL_OPENGL)
1251   // grab the viewport dimensions and location
1252   GLint viewport[4];
1253   BeginPaint();
1254   glGetIntegerv(GL_VIEWPORT, viewport);
1255
1256   float w = (float)viewport[2]*0.5f;
1257   float h = (float)viewport[3]*0.5f;
1258
1259   glMatrixMode(GL_MODELVIEW);
1260   glLoadIdentity();
1261   glTranslatef(-(viewport[0] + w + offset.x), +(viewport[1] + h + offset.y), 0);  
1262   gluLookAt(0.0, 0.0, -2.0*h, 0.0, 0.0, 0.0, 0.0, -1.0, 0.0);
1263   glMatrixMode(GL_PROJECTION);
1264   glLoadIdentity();
1265   glFrustum( (-w - offset.x)*0.5f, (w - offset.x)*0.5f, (-h + offset.y)*0.5f, (h + offset.y)*0.5f, h, 100*h);
1266   glMatrixMode(GL_MODELVIEW);
1267   EndPaint();
1268 #endif
1269 }
1270
1271 bool CGraphicContext::RectIsAngled(float x1, float y1, float x2, float y2) const
1272 { // need only test 3 points, as they must be co-planer
1273   if (m_finalTransform.TransformZCoord(x1, y1, 0)) return true;
1274   if (m_finalTransform.TransformZCoord(x2, y2, 0)) return true;
1275   if (m_finalTransform.TransformZCoord(x1, y2, 0)) return true;
1276   return false;
1277 }
1278
1279 float CGraphicContext::GetFPS() const
1280 {
1281 #ifdef __APPLE__
1282   // Get the actual refresh rate of the display
1283   return Cocoa_GetScreenRefreshRate(g_settings.m_ResInfo[m_Resolution].iScreen);
1284 #else
1285   if (g_settings.m_ResInfo[m_Resolution].fRefreshRate > 0)
1286     return g_settings.m_ResInfo[m_Resolution].fRefreshRate;
1287   if (m_Resolution == PAL_4x3 || m_Resolution == PAL_16x9)
1288     return 50.0f;
1289   if (m_Resolution == HDTV_1080i)
1290     return 30.0f;
1291   return 60.0f;
1292 #endif
1293 }
1294
1295 #ifdef HAS_SDL_2D
1296 int CGraphicContext::BlitToScreen(SDL_Surface *src, SDL_Rect *srcrect, SDL_Rect *dstrect)
1297 {
1298   return SDL_BlitSurface(src, srcrect, m_screenSurface->SDL(), dstrect);
1299 }
1300 #endif
1301
1302 #ifdef HAS_SDL_OPENGL
1303 #ifdef  __GNUC__
1304 // TODO: CGraphicContext needs to cleanup unused surfaces
1305 #endif
1306 bool CGraphicContext::ValidateSurface(CSurface* dest)
1307 {
1308   CSingleLock aLock(m_surfaceLock);
1309   map<Uint32, CSurface*>::iterator iter;
1310   Uint32 tid = SDL_ThreadID();
1311   iter = m_surfaces.find(tid);
1312   if (iter==m_surfaces.end()) {
1313 #if defined(HAS_GLX) || defined(__APPLE__) || defined(_WIN32PC)
1314     if (dest==NULL)
1315     {
1316       CLog::Log(LOGDEBUG, "GL: Sharing screen surface for thread %u", tid);
1317       CSurface* surface = new CSurface(m_screenSurface);
1318       if (!surface->MakeCurrent())
1319       {
1320         CLog::Log(LOGERROR, "GL: Error making context current");
1321         delete surface;
1322         return false;
1323       }
1324       m_surfaces[tid] = surface;
1325       return true;
1326     } 
1327     else 
1328     {
1329       m_surfaces[tid] = dest;
1330       dest->MakeCurrent();
1331     }
1332 #else
1333     CLog::Log(LOGDEBUG, "Creating surface for thread %ul", tid);
1334     CSurface* surface = InitializeSurface();
1335     if (surface) 
1336     {
1337       m_surfaces[tid] = surface;
1338       return true;
1339     } else {
1340       CLog::Log(LOGERROR, "Did not get surface for thread %ul", tid);
1341       return false;
1342     }
1343 #endif
1344   } else {
1345     (iter->second)->MakeCurrent();
1346   }
1347   return true;
1348 }
1349
1350 CSurface* CGraphicContext::InitializeSurface()
1351 {
1352   CSurface* screenSurface = NULL;
1353   Lock();
1354
1355   screenSurface = new CSurface(m_iScreenWidth, m_iScreenHeight, true, m_screenSurface, m_screenSurface);
1356   if (!screenSurface || !screenSurface->IsValid()) 
1357   {
1358     CLog::Log(LOGERROR, "Surface creation error");
1359     if (screenSurface) 
1360     {
1361       delete screenSurface;
1362     }
1363     Unlock();
1364     return NULL;
1365   }
1366   glClearColor( 0.0f, 0.0f, 0.0f, 0.0f );
1367   
1368   glViewport(0, 0, m_iScreenWidth, m_iScreenHeight);
1369   glScissor(0, 0, m_iScreenWidth, m_iScreenHeight);
1370   glEnable(GL_TEXTURE_2D); 
1371   glEnable(GL_SCISSOR_TEST); 
1372   
1373   glMatrixMode(GL_PROJECTION);
1374   glLoadIdentity();
1375  
1376   glOrtho(0.0f, m_iScreenWidth-1, m_iScreenHeight-1, 0.0f, -1.0f, 1.0f);
1377
1378   glMatrixMode(GL_MODELVIEW);
1379   glLoadIdentity(); 
1380   
1381   glBlendFunc(GL_SRC_ALPHA, GL_ONE);
1382   glEnable(GL_BLEND);          // Turn Blending On
1383   glDisable(GL_DEPTH_TEST);
1384
1385   Unlock();
1386   return screenSurface;
1387 }
1388
1389 #endif
1390
1391 void CGraphicContext::ReleaseCurrentContext(Surface::CSurface* ctx)
1392 {
1393 #ifdef HAS_SDL_OPENGL
1394   if (ctx)
1395   {
1396     Lock();
1397     ctx->ReleaseContext();
1398     Unlock();
1399     return;
1400   }
1401   Lock();
1402   map<Uint32, CSurface*>::iterator iter;
1403   Uint32 tid = SDL_ThreadID();
1404   CSingleLock aLock(m_surfaceLock);
1405   iter = m_surfaces.find(tid);
1406   if (iter==m_surfaces.end()) 
1407   {
1408     m_screenSurface->ReleaseContext();
1409     Unlock();
1410     return;
1411   }
1412   (iter->second)->ReleaseContext();
1413   Unlock();
1414 #endif
1415 }
1416
1417 void CGraphicContext::DeleteThreadContext() {
1418 #ifdef HAS_SDL_OPENGL
1419   CSingleLock aLock(m_surfaceLock);
1420   map<Uint32, CSurface*>::iterator iter;
1421   Uint32 tid = SDL_ThreadID();
1422   iter = m_surfaces.find(tid);
1423   if (iter!=m_surfaces.end()) 
1424     m_surfaces.erase(iter);
1425 #endif
1426 }
1427
1428 void CGraphicContext::AcquireCurrentContext(Surface::CSurface* ctx)
1429 {
1430 #ifdef HAS_SDL_OPENGL
1431   if (ctx)
1432   {
1433     Lock();
1434     if (!ctx->MakeCurrent())
1435     {
1436       CLog::Log(LOGERROR, "Error making context current");
1437     }
1438     Unlock();
1439     return;
1440   }
1441   Lock();
1442   map<Uint32, CSurface*>::iterator iter;
1443   Uint32 tid = SDL_ThreadID();
1444   CSingleLock aLock(m_surfaceLock);
1445   iter = m_surfaces.find(tid);
1446   if (iter==m_surfaces.end()) 
1447   {
1448     Unlock();
1449     return;
1450   }
1451   if (!(iter->second)->MakeCurrent())
1452   {
1453     CLog::Log(LOGERROR, "Error making context current");
1454   }
1455   Unlock();
1456 #endif
1457 }
1458
1459 void CGraphicContext::BeginPaint(CSurface *dest, bool lock)
1460 {
1461 #ifdef HAS_SDL_OPENGL
1462   if (lock) Lock();
1463   ValidateSurface(dest);
1464   VerifyGLState();
1465 #endif
1466 }
1467
1468 void CGraphicContext::EndPaint(CSurface *dest, bool lock)
1469 {
1470 #ifdef HAS_SDL_OPENGL
1471   if (lock) Unlock();
1472   VerifyGLState();
1473 #endif
1474 }
1475
1476 bool CGraphicContext::ToggleFullScreenRoot ()
1477 {
1478 #ifndef _WIN32PC
1479   // FIXME: see if #5256 works also for Linux and Mac
1480   static RESOLUTION desktopres = DESKTOP;
1481   static RESOLUTION windowres = WINDOW;
1482   static RESOLUTION lastres = INVALID;
1483   if (m_bFullScreenRoot)
1484   {
1485     lastres = GetVideoResolution();
1486     SetVideoResolution(windowres);
1487   }
1488   else
1489   {
1490     if (lastres != INVALID)
1491     {
1492       SetVideoResolution(lastres);
1493     }
1494     else
1495     {
1496       SetVideoResolution(desktopres);
1497     }
1498   }
1499   return  m_bFullScreenRoot;
1500 #else
1501   if (m_bFullScreenRoot)
1502   {
1503     g_advancedSettings.m_fullScreen = false;
1504     SetVideoResolution(m_Resolution);
1505   }
1506   else
1507   {
1508     g_advancedSettings.m_fullScreen = true;
1509     SetVideoResolution(m_Resolution);
1510   }
1511   return  m_bFullScreenRoot;
1512 #endif
1513 }
1514
1515 void CGraphicContext::SetFullScreenRoot(bool fs)
1516 {
1517 #ifdef __APPLE__
1518   int blanking = g_guiSettings.GetInt("videoscreen.displayblanking");
1519   bool blankOtherDisplays = (blanking == BLANKING_ALL_DISPLAYS);
1520 #endif
1521   
1522   if (fs)
1523   {
1524     // Code from this point on should be platform dependent. The Win32 version could
1525     // probably use GetSystemMetrics/EnumDisplayDevices/GetDeviceCaps to query current 
1526     // resolution on the requested display no. and set 'width' and 'height'
1527     
1528     m_iFullScreenWidth = m_iScreenWidth;
1529     m_iFullScreenHeight = m_iScreenHeight;
1530 #ifdef HAS_XRANDR
1531     XOutput out;
1532     XMode mode;
1533     RESOLUTION res = m_Resolution;
1534     out.name = g_settings.m_ResInfo[res].strOutput;
1535     mode.w = g_settings.m_ResInfo[res].iWidth;
1536     mode.h = g_settings.m_ResInfo[res].iHeight;
1537     mode.hz = g_settings.m_ResInfo[res].fRefreshRate;
1538     mode.id = g_settings.m_ResInfo[res].strId;
1539     g_xrandr.SetMode(out, mode);
1540     SDL_ShowCursor(SDL_ENABLE);    
1541 #endif
1542 #if defined(__APPLE__)
1543     Cocoa_GL_SetFullScreen(m_iFullScreenWidth, m_iFullScreenHeight, true, blankOtherDisplays, g_advancedSettings.m_osx_GLFullScreen);
1544 #elif defined(_WIN32PC)
1545     DEVMODE settings;
1546     settings.dmSize = sizeof(settings);
1547     settings.dmDriverExtra = 0;
1548     settings.dmBitsPerPel = 32;
1549     settings.dmPelsWidth = m_iFullScreenWidth;
1550     settings.dmPelsHeight = m_iFullScreenHeight;
1551     settings.dmDisplayFrequency = (int)floorf(g_settings.m_ResInfo[m_Resolution].fRefreshRate);
1552     settings.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL;
1553     if(settings.dmDisplayFrequency)
1554       settings.dmFields |= DM_DISPLAYFREQUENCY;
1555     if(ChangeDisplaySettings(&settings, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL)
1556       CLog::Log(LOGERROR, "CGraphicContext::SetFullScreenRoot - failed to change resolution");
1557 #else
1558     SDL_SetVideoMode(m_iFullScreenWidth, m_iFullScreenHeight, 0, SDL_FULLSCREEN);
1559 #endif
1560     m_screenSurface->RefreshCurrentContext();
1561     m_screenSurface->ResizeSurface(m_iFullScreenWidth, m_iFullScreenHeight);
1562 #ifdef HAS_SDL_OPENGL
1563     glViewport(0, 0, m_iFullScreenWidth, m_iFullScreenHeight);
1564     glScissor(0, 0, m_iFullScreenWidth, m_iFullScreenHeight);
1565 #endif
1566     g_fontManager.ReloadTTFFonts();
1567     g_Mouse.SetResolution(m_iFullScreenWidth, m_iFullScreenHeight, 1, 1);
1568   }
1569   else
1570   {
1571 #ifdef __APPLE__
1572     Cocoa_GL_SetFullScreen(
1573       g_settings.m_ResInfo[m_Resolution].iWidth,
1574       g_settings.m_ResInfo[m_Resolution].iHeight,
1575       false, blankOtherDisplays, g_advancedSettings.m_osx_GLFullScreen);
1576 #elif defined(_WIN32PC)
1577     ChangeDisplaySettings(NULL, 0);
1578 #else
1579     SDL_SetVideoMode(m_iScreenWidth, m_iScreenHeight, 0, SDL_RESIZABLE);
1580 #endif
1581     m_screenSurface->RefreshCurrentContext();
1582     m_screenSurface->ResizeSurface(m_iScreenWidth, m_iScreenHeight);
1583
1584 #ifdef HAS_SDL_OPENGL
1585     glViewport(0, 0, m_iScreenWidth, m_iScreenHeight);
1586     glScissor(0, 0, m_iScreenWidth, m_iScreenHeight);
1587 #endif
1588     g_fontManager.ReloadTTFFonts();
1589     g_Mouse.SetResolution(g_settings.m_ResInfo[m_Resolution].iWidth, g_settings.m_ResInfo[m_Resolution].iHeight, 1, 1);
1590   }
1591
1592   m_bFullScreenRoot = fs;
1593   g_advancedSettings.m_fullScreen = fs;
1594   SetFullScreenViewWindow(m_Resolution);
1595
1596 // The _correct_ way to switch to fullscreen in X. Doesn't work now because of the way
1597 // SDL creates windows. Should be fixed in SDL 1.3 therefore currently disabled.
1598 #if 0
1599   enum
1600   {
1601     _NET_WM_STATE_REMOVE = 0,
1602     _NET_WM_STATE_ADD = 1,
1603     _NET_WM_STATE_TOGGLE = 2    
1604   };
1605  
1606   SDL_SysWMinfo info;
1607   SDL_VERSION(&info.version);
1608   SDL_GetWMInfo(&info);
1609   glXWaitX();
1610
1611   XEvent xev;
1612   Atom stateAtom, fullScreenAtom;
1613   Display * pRootDisplay = XOpenDisplay(NULL);
1614   int screen = DefaultScreen(pRootDisplay); 
1615   Window tempwindow, parent, glparent = info.info.x11.window;
1616   Window *children = NULL;
1617   unsigned int numchildren;
1618
1619   // get the real parent window
1620   Window previousparent;
1621   do 
1622   {
1623     XQueryTree(pRootDisplay, glparent, &tempwindow, &parent, &children, &numchildren);
1624     if (parent==tempwindow)
1625       break;
1626     previousparent = glparent;
1627     glparent = parent;
1628   } while (1);
1629   
1630   glparent = previousparent;
1631   stateAtom = XInternAtom(pRootDisplay, "_NET_WM_STATE", False);
1632   fullScreenAtom = XInternAtom(pRootDisplay, "_NET_WM_STATE_FULLSCREEN", False);
1633   
1634   xev.xclient.type = ClientMessage;
1635   xev.xclient.serial = 0;
1636   xev.xclient.send_event = True;
1637   xev.xclient.window = glparent;
1638   xev.xclient.message_type = stateAtom;
1639   xev.xclient.format = 32;
1640   xev.xclient.data.l[0] = (fs ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE);
1641   xev.xclient.data.l[1] = fullScreenAtom;
1642   xev.xclient.data.l[2] = 0;
1643
1644   XChangeProperty(pRootDisplay, glparent, stateAtom, XA_ATOM, 32, PropModeReplace,
1645                   (unsigned char *)&fullScreenAtom, 1);
1646   if (XSendEvent(pRootDisplay, DefaultRootWindow(pRootDisplay), False, 
1647                  SubstructureRedirectMask | SubstructureNotifyMask, 
1648                  &xev))
1649   {
1650     m_bFullScreenRoot = fs;
1651   }
1652   glXWaitX();
1653 #endif
1654 }
1655
1656 void CGraphicContext::SetMediaDir(const CStdString &strMediaDir)
1657 {
1658   g_TextureManager.SetTexturePath(strMediaDir);
1659   m_strMediaDir = strMediaDir;
1660 }
1661
1662 void CGraphicContext::Flip()
1663 {
1664   m_screenSurface->Flip();
1665 }
1666
1667 void CGraphicContext::ApplyHardwareTransform()
1668 {
1669 #ifdef HAS_SDL_OPENGL
1670   glMatrixMode(GL_MODELVIEW);
1671   glPushMatrix();
1672   GLfloat matrix[4][4];
1673
1674   for(int i=0;i<3;i++)
1675     for(int j=0;j<4;j++)
1676       matrix[j][i] = m_finalTransform.m[i][j];
1677
1678   matrix[0][3] = 0.0f;
1679   matrix[1][3] = 0.0f;
1680   matrix[2][3] = 0.0f;
1681   matrix[3][3] = 1.0f;
1682
1683   glMultMatrixf(&matrix[0][0]);
1684 #endif
1685 }
1686
1687 void CGraphicContext::RestoreHardwareTransform()
1688 {
1689 #ifdef HAS_SDL_OPENGL
1690   glMatrixMode(GL_MODELVIEW);
1691   glPopMatrix();
1692 #endif
1693 }
1694
1695 void CGraphicContext::NotifyAppFocusChange(bool bGaining)
1696 {
1697   /* Notification from the Application that we are either becoming the foreground window or are losing focus */
1698   if (m_screenSurface)
1699     m_screenSurface->NotifyAppFocusChange(bGaining);
1700 }
1701