diff --git a/xbmc/Application.cpp b/xbmc/Application.cpp index 961e8f9d22737..1162722324b08 100644 --- a/xbmc/Application.cpp +++ b/xbmc/Application.cpp @@ -794,7 +794,7 @@ bool CApplication::CreateGUI() uint32_t sdlFlags = 0; -#if defined(HAS_SDL_OPENGL) || (HAS_GLES == 2) +#if (defined(HAS_SDL_OPENGL) || (HAS_GLES == 2)) && !defined(HAS_GLX) sdlFlags |= SDL_INIT_VIDEO; #endif diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/VDPAU.cpp b/xbmc/cores/dvdplayer/DVDCodecs/Video/VDPAU.cpp index f70a4f972cfe5..89711079b5c46 100644 --- a/xbmc/cores/dvdplayer/DVDCodecs/Video/VDPAU.cpp +++ b/xbmc/cores/dvdplayer/DVDCodecs/Video/VDPAU.cpp @@ -277,7 +277,7 @@ bool CVDPAU::MakePixmapGL() }; GLXFBConfig *fbConfigs; - fbConfigs = glXChooseFBConfig(m_Display, DefaultScreen(m_Display), doubleVisAttributes, &num); + fbConfigs = glXChooseFBConfig(m_Display, g_Windowing.GetVisual()->screen, doubleVisAttributes, &num); if (fbConfigs==NULL) { CLog::Log(LOGERROR, "GLX Error: MakePixmap: No compatible framebuffers found"); @@ -328,10 +328,10 @@ bool CVDPAU::MakePixmap(int width, int height) // Get our window attribs. XWindowAttributes wndattribs; - XGetWindowAttributes(m_Display, DefaultRootWindow(m_Display), &wndattribs); // returns a status but I don't know what success is + XGetWindowAttributes(m_Display, g_Windowing.GetWindow(), &wndattribs); // returns a status but I don't know what success is m_Pixmap = XCreatePixmap(m_Display, - DefaultRootWindow(m_Display), + g_Windowing.GetWindow(), OutWidth, OutHeight, wndattribs.depth); @@ -343,7 +343,7 @@ bool CVDPAU::MakePixmap(int width, int height) XGCValues values = {}; GC xgc; - values.foreground = BlackPixel (m_Display, DefaultScreen (m_Display)); + values.foreground = BlackPixel (m_Display, g_Windowing.GetVisual()->screen); xgc = XCreateGC(m_Display, m_Pixmap, GCForeground, &values); XFillRectangle(m_Display, m_Pixmap, xgc, 0, 0, OutWidth, OutHeight); XFreeGC(m_Display, xgc); @@ -755,7 +755,7 @@ void CVDPAU::InitVDPAUProcs() return; } - int mScreen = DefaultScreen(m_Display); + int mScreen = g_Windowing.GetVisual()->screen; VdpStatus vdp_st; // Create Device diff --git a/xbmc/guilib/Resolution.h b/xbmc/guilib/Resolution.h index ede938da96b0d..e02f2888fcd29 100644 --- a/xbmc/guilib/Resolution.h +++ b/xbmc/guilib/Resolution.h @@ -95,6 +95,7 @@ struct RESOLUTION_INFO CStdString strMode; CStdString strOutput; CStdString strId; + int iInternal; /* internal to windowing layer */ public: RESOLUTION_INFO(int width = 1280, int height = 720, float aspect = 0, const CStdString &mode = "") { @@ -105,6 +106,7 @@ struct RESOLUTION_INFO bFullScreen = true; fRefreshRate = 0; dwFlags = iSubtitles = iScreen = 0; + iInternal = 0; } float DisplayRatio() const { @@ -117,5 +119,6 @@ struct RESOLUTION_INFO iSubtitles = res.iSubtitles; dwFlags = res.dwFlags; fPixelRatio = res.fPixelRatio; fRefreshRate = res.fRefreshRate; strMode = res.strMode; strOutput = res.strOutput; strId = res.strId; + iInternal = res.iInternal; } }; diff --git a/xbmc/input/XBMC_keytable.cpp b/xbmc/input/XBMC_keytable.cpp index dbc28a7e574a6..0ab6e78d3c4b0 100644 --- a/xbmc/input/XBMC_keytable.cpp +++ b/xbmc/input/XBMC_keytable.cpp @@ -179,6 +179,8 @@ static const XBMCKEYTABLE XBMCKeyTable[] = , { XBMCK_LAUNCH_APP2, 0, 0, XBMCVK_LAUNCH_APP2, "launch_app2_pc_icon" } , { XBMCK_LAUNCH_FILE_BROWSER, 0, 0, XBMCVK_LAUNCH_FILE_BROWSER, "launch_file_browser" } , { XBMCK_LAUNCH_MEDIA_CENTER, 0, 0, XBMCVK_LAUNCH_MEDIA_CENTER, "launch_media_center" } +, { XBMCK_PLAY, 0, 0, XBMCVK_MEDIA_PLAY_PAUSE, "play_pause" } +, { XBMCK_STOP, 0, 0, XBMCVK_MEDIA_STOP, "stop" } // Function keys , { XBMCK_F1, 0, 0, XBMCVK_F1, "f1"} diff --git a/xbmc/rendering/gl/RenderSystemGL.cpp b/xbmc/rendering/gl/RenderSystemGL.cpp index 4f52d3563b7e0..5ac1403cae087 100644 --- a/xbmc/rendering/gl/RenderSystemGL.cpp +++ b/xbmc/rendering/gl/RenderSystemGL.cpp @@ -175,6 +175,7 @@ bool CRenderSystemGL::ResetRenderSystem(int width, int height, bool fullScreen, { m_width = width; m_height = height; + m_bVsyncInit = false; glClearColor( 0.0f, 0.0f, 0.0f, 0.0f ); diff --git a/xbmc/system.h b/xbmc/system.h index 60eab164d6551..4f9720ceb93b7 100644 --- a/xbmc/system.h +++ b/xbmc/system.h @@ -162,16 +162,21 @@ #define HAS_GL #ifdef HAVE_X11 #define HAS_GLX +#define HAS_X11_WIN_EVENTS #endif #ifdef HAVE_SDL #define HAS_SDL #ifndef HAS_SDL_OPENGL #define HAS_SDL_OPENGL #endif +#ifndef HAS_X11_WIN_EVENTS #define HAS_SDL_WIN_EVENTS +#endif #else +#ifndef HAS_X11_WIN_EVENTS #define HAS_LINUX_EVENTS #endif +#endif #define HAS_LINUX_NETWORK #define HAS_LIRC #ifdef HAVE_LIBPULSE diff --git a/xbmc/windowing/Makefile b/xbmc/windowing/Makefile index f109bec61d8c2..f98164264c050 100644 --- a/xbmc/windowing/Makefile +++ b/xbmc/windowing/Makefile @@ -1,6 +1,7 @@ SRCS=WinEventsSDL.cpp \ WinEventsLinux.cpp \ WinSystem.cpp \ + WinEventsX11.cpp \ LIB=windowing.a diff --git a/xbmc/windowing/WinEvents.h b/xbmc/windowing/WinEvents.h index 6d322a92e402b..5a671cc06cfeb 100644 --- a/xbmc/windowing/WinEvents.h +++ b/xbmc/windowing/WinEvents.h @@ -58,6 +58,10 @@ class CWinEventsBase #include "WinEventsSDL.h" #define CWinEvents CWinEventsSDL +#elif defined(TARGET_LINUX) && defined(HAS_X11_WIN_EVENTS) +#include "WinEventsX11.h" +#define CWinEvents CWinEventsX11 + #elif defined(TARGET_LINUX) && defined(HAS_LINUX_EVENTS) #include "WinEventsLinux.h" #define CWinEvents CWinEventsLinux diff --git a/xbmc/windowing/WinEventsX11.cpp b/xbmc/windowing/WinEventsX11.cpp new file mode 100644 index 0000000000000..cb6d22ce87b03 --- /dev/null +++ b/xbmc/windowing/WinEventsX11.cpp @@ -0,0 +1,750 @@ +/* +* Copyright (C) 2005-2012 Team XBMC +* http://www.xbmc.org +* +* This Program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2, or (at your option) +* any later version. +* +* This Program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with XBMC; see the file COPYING. If not, write to +* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +* http://www.gnu.org/copyleft/gpl.html +* +*/ + +#include "system.h" + +#ifdef HAS_X11_WIN_EVENTS + +#include "WinEvents.h" +#include "WinEventsX11.h" +#include "Application.h" +#include +#include "X11/WinSystemX11GL.h" +#include "X11/keysymdef.h" +#include "X11/XF86keysym.h" +#include "utils/log.h" +#include "utils/CharsetConverter.h" +#include "guilib/GUIWindowManager.h" +#include "input/MouseStat.h" +#include "ApplicationMessenger.h" + +#if defined(HAS_XRANDR) +#include +#endif + +#ifdef HAS_SDL_JOYSTICK +#include "input/SDLJoystick.h" +#endif + +CWinEventsX11* CWinEventsX11::WinEvents = 0; + +static const uint32_t SymMappingsX11[][2] = +{ + {XK_BackSpace, XBMCK_BACKSPACE} +, {XK_Tab, XBMCK_TAB} +, {XK_Clear, XBMCK_CLEAR} +, {XK_Return, XBMCK_RETURN} +, {XK_Pause, XBMCK_PAUSE} +, {XK_Escape, XBMCK_ESCAPE} +, {XK_Delete, XBMCK_DELETE} +// multi-media keys +, {XF86XK_Back, XBMCK_BROWSER_BACK} +, {XF86XK_Forward, XBMCK_BROWSER_FORWARD} +, {XF86XK_Refresh, XBMCK_BROWSER_REFRESH} +, {XF86XK_Stop, XBMCK_BROWSER_STOP} +, {XF86XK_Search, XBMCK_BROWSER_SEARCH} +, {XF86XK_Favorites, XBMCK_BROWSER_FAVORITES} +, {XF86XK_HomePage, XBMCK_BROWSER_HOME} +, {XF86XK_AudioMute, XBMCK_VOLUME_MUTE} +, {XF86XK_AudioLowerVolume, XBMCK_VOLUME_DOWN} +, {XF86XK_AudioRaiseVolume, XBMCK_VOLUME_UP} +, {XF86XK_AudioNext, XBMCK_MEDIA_NEXT_TRACK} +, {XF86XK_AudioPrev, XBMCK_MEDIA_PREV_TRACK} +, {XF86XK_AudioStop, XBMCK_MEDIA_STOP} +, {XF86XK_AudioPause, XBMCK_MEDIA_PLAY_PAUSE} +, {XF86XK_Mail, XBMCK_LAUNCH_MAIL} +, {XF86XK_Select, XBMCK_LAUNCH_MEDIA_SELECT} +, {XF86XK_Launch0, XBMCK_LAUNCH_APP1} +, {XF86XK_Launch1, XBMCK_LAUNCH_APP2} +, {XF86XK_WWW, XBMCK_LAUNCH_FILE_BROWSER} +, {XF86XK_AudioMedia, XBMCK_LAUNCH_MEDIA_CENTER } + // Numeric keypad +, {XK_KP_0, XBMCK_KP0} +, {XK_KP_1, XBMCK_KP1} +, {XK_KP_2, XBMCK_KP2} +, {XK_KP_3, XBMCK_KP3} +, {XK_KP_4, XBMCK_KP4} +, {XK_KP_5, XBMCK_KP5} +, {XK_KP_6, XBMCK_KP6} +, {XK_KP_7, XBMCK_KP7} +, {XK_KP_8, XBMCK_KP8} +, {XK_KP_9, XBMCK_KP9} +, {XK_KP_Separator, XBMCK_KP_PERIOD} +, {XK_KP_Divide, XBMCK_KP_DIVIDE} +, {XK_KP_Multiply, XBMCK_KP_MULTIPLY} +, {XK_KP_Subtract, XBMCK_KP_MINUS} +, {XK_KP_Add, XBMCK_KP_PLUS} +, {XK_KP_Enter, XBMCK_KP_ENTER} +, {XK_KP_Equal, XBMCK_KP_EQUALS} + // Arrows + Home/End pad +, {XK_Up, XBMCK_UP} +, {XK_Down, XBMCK_DOWN} +, {XK_Right, XBMCK_RIGHT} +, {XK_Left, XBMCK_LEFT} +, {XK_Insert, XBMCK_INSERT} +, {XK_Home, XBMCK_HOME} +, {XK_End, XBMCK_END} +, {XK_Page_Up, XBMCK_PAGEUP} +, {XK_Page_Down, XBMCK_PAGEDOWN} + // Function keys +, {XK_F1, XBMCK_F1} +, {XK_F2, XBMCK_F2} +, {XK_F3, XBMCK_F3} +, {XK_F4, XBMCK_F4} +, {XK_F5, XBMCK_F5} +, {XK_F6, XBMCK_F6} +, {XK_F7, XBMCK_F7} +, {XK_F8, XBMCK_F8} +, {XK_F9, XBMCK_F9} +, {XK_F10, XBMCK_F10} +, {XK_F11, XBMCK_F11} +, {XK_F12, XBMCK_F12} +, {XK_F13, XBMCK_F13} +, {XK_F14, XBMCK_F14} +, {XK_F15, XBMCK_F15} + // Key state modifier keys +, {XK_Num_Lock, XBMCK_NUMLOCK} +, {XK_Caps_Lock, XBMCK_CAPSLOCK} +, {XK_Scroll_Lock, XBMCK_SCROLLOCK} +, {XK_Shift_R, XBMCK_RSHIFT} +, {XK_Shift_L, XBMCK_LSHIFT} +, {XK_Control_R, XBMCK_RCTRL} +, {XK_Control_L, XBMCK_LCTRL} +, {XK_Alt_R, XBMCK_RALT} +, {XK_Alt_L, XBMCK_LALT} +, {XK_Meta_R, XBMCK_RMETA} +, {XK_Meta_L, XBMCK_LMETA} +, {XK_Super_L, XBMCK_LSUPER} +, {XK_Super_R, XBMCK_RSUPER} +, {XK_Mode_switch, XBMCK_MODE} +, {XK_Multi_key, XBMCK_COMPOSE} + // Miscellaneous function keys +, {XK_Help, XBMCK_HELP} +, {XK_Print, XBMCK_PRINT} +//, {0, XBMCK_SYSREQ} +, {XK_Break, XBMCK_BREAK} +, {XK_Menu, XBMCK_MENU} +, {XF86XK_PowerOff, XBMCK_POWER} +, {XK_EcuSign, XBMCK_EURO} +, {XK_Undo, XBMCK_UNDO} + /* Media keys */ +, {XF86XK_Eject, XBMCK_EJECT} +, {XF86XK_Stop, XBMCK_STOP} +, {XF86XK_AudioRecord, XBMCK_RECORD} +, {XF86XK_AudioRewind, XBMCK_REWIND} +, {XF86XK_Phone, XBMCK_PHONE} +, {XF86XK_AudioPlay, XBMCK_PLAY} +, {XF86XK_AudioRandomPlay, XBMCK_SHUFFLE} +, {XF86XK_AudioForward, XBMCK_FASTFORWARD} +}; + +CWinEventsX11::~CWinEventsX11() +{ + free(m_keybuf); + m_keybuf = 0; + + if (m_xic) + { + XUnsetICFocus(m_xic); + XDestroyIC(m_xic); + m_xic = 0; + } + + if (m_xim) + { + XCloseIM(m_xim); + m_xim = 0; + } + + m_symLookupTable.clear(); +} + +bool CWinEventsX11::Init(Display *dpy, Window win) +{ + if (WinEvents) + return true; + WinEvents = new CWinEventsX11(dpy, win); + return true; +} + +CWinEventsX11::CWinEventsX11(Display *dpy, Window win) +{ + m_display = dpy; + m_window = win; + m_keybuf_len = 32*sizeof(char); + m_keybuf = (char*)malloc(m_keybuf_len); + m_keymodState = 0; + m_wmDeleteMessage = XInternAtom(dpy, "WM_DELETE_WINDOW", False); + memset(&m_compose, 0, sizeof(m_compose)); + + // open input method + CStdString old_locale, old_modifiers; + char res_name[] = "xbmc"; + + // save current locale, this should be "C" + old_locale = setlocale(LC_ALL, NULL); + old_modifiers = XSetLocaleModifiers(NULL); + + // set users preferences and open input method + setlocale(LC_ALL, ""); + XSetLocaleModifiers(""); + + m_xim = XOpenIM(m_display, NULL, res_name, res_name); + + // restore original locale + XSetLocaleModifiers(old_modifiers.c_str()); + setlocale(LC_ALL, old_locale.c_str()); + + m_xic = NULL; + if (m_xim) + { + m_xic = XCreateIC(m_xim, + XNClientWindow , m_window, + XNFocusWindow , m_window, + XNInputStyle , XIMPreeditNothing | XIMStatusNothing, + XNResourceName , res_name, + XNResourceClass, res_name, + NULL); + } + + if (!m_xic) + CLog::Log(LOGWARNING,"CWinEventsX11::Init - no input method found"); + + // build Keysym lookup table + for (unsigned int i = 0; i < sizeof(SymMappingsX11)/(2*sizeof(uint32_t)); ++i) + { + m_symLookupTable[SymMappingsX11[i][0]] = SymMappingsX11[i][1]; + } + + // register for xrandr events +#if defined(HAS_XRANDR) + int iReturn; + XRRQueryExtension(m_display, &m_RREventBase, &iReturn); + XRRSelectInput(m_display, m_window, RRScreenChangeNotifyMask); +#endif +} + +void CWinEventsX11::Quit() +{ + if (!WinEvents) + return; + + delete WinEvents; + WinEvents = NULL; +} + +bool CWinEventsX11::MessagePump() +{ + if (!WinEvents) + return false; + return WinEvents->Process(); +} + +bool CWinEventsX11::ProcessKeyPress(XKeyEvent& xevent) +{ + XBMC_Event newEvent = {0}; + KeySym xkeysym; + bool ret = false; + Status status; + int len; + CStdString data; + CStdStringW keys; + + // fallback if we have no IM + if (m_xic) + { + len = Xutf8LookupString(m_xic, &xevent, + m_keybuf, m_keybuf_len, + &xkeysym, &status); + if (status == XBufferOverflow) + { + m_keybuf_len = len; + m_keybuf = (char*)realloc(m_keybuf, m_keybuf_len); + len = Xutf8LookupString(m_xic, &xevent, + m_keybuf, m_keybuf_len, + &xkeysym, &status); + } + data.assign(m_keybuf, len); + g_charsetConverter.utf8ToW(data, keys, false); + } + else + { + len = XLookupString(&xevent, m_keybuf, m_keybuf_len, &xkeysym, &m_compose); + if(len > 0) + status = XLookupBoth; + else + status = XLookupKeySym; + data.assign(m_keybuf, len); + g_charsetConverter.toW(data, keys, "LATIN1"); + } + + + /* apparently Xutf8LookupString will not always return + * the expected keysym for Ctrl+S and will instead + * return Ctrl+s (lowercase). This causes keymap + * issues. Investigation needed to see if it can be + * removed + */ + xkeysym = XLookupKeysym(&xevent, 0); + + switch (status) + { + case XLookupNone: + break; + case XLookupChars: + case XLookupBoth: + { + if (keys.length() == 0) + { + break; + } + + for (unsigned int i = 0; i < keys.length() - 1; i++) + { + newEvent.key.keysym.sym = XBMCK_UNKNOWN; + newEvent.key.keysym.unicode = keys[i]; + newEvent.key.state = XBMC_PRESSED; + newEvent.key.type = XBMC_KEYDOWN; + ret |= ProcessKey(newEvent); + } + if (keys.length() > 0) + { + newEvent.key.keysym.scancode = xevent.keycode; + newEvent.key.keysym.sym = LookupXbmcKeySym(xkeysym); + newEvent.key.keysym.unicode = keys[keys.length() - 1]; + newEvent.key.state = XBMC_PRESSED; + newEvent.key.type = XBMC_KEYDOWN; + ret |= ProcessKey(newEvent); + } + break; + } + + case XLookupKeySym: + { + newEvent.key.keysym.scancode = xevent.keycode; + newEvent.key.keysym.sym = LookupXbmcKeySym(xkeysym); + newEvent.key.state = XBMC_PRESSED; + newEvent.key.type = XBMC_KEYDOWN; + ret |= ProcessKey(newEvent); + break; + } + + } + + return ret; +} + +bool CWinEventsX11::ProcessKeyRelease(XKeyEvent& xkey) +{ + /* if we have a queued press directly after, this is a repeat */ + if( XEventsQueued( m_display, QueuedAfterReading ) ) + { + XEvent next_event; + XPeekEvent( m_display, &next_event ); + if(next_event.type == KeyPress + && next_event.xkey.window == xkey.window + && next_event.xkey.keycode == xkey.keycode + && next_event.xkey.time == xkey.time ) + return false; + } + + XBMC_Event newEvent = {0}; + newEvent.key.keysym.scancode = xkey.keycode; + newEvent.key.keysym.sym = LookupXbmcKeySym(XLookupKeysym(&xkey, 0)); + newEvent.key.state = XBMC_RELEASED; + newEvent.key.type = XBMC_KEYUP; + return ProcessKey(newEvent); +} + +bool CWinEventsX11::ProcessConfigure (XConfigureEvent& xevent) +{ + if (xevent.window != m_window) + return false; + + bool ret = false; + + /* find the last configure event in the queue */ + while(XCheckTypedWindowEvent(m_display, m_window, ConfigureNotify, (XEvent*)&xevent)) + ; + + /* check for resize */ + if((int)g_Windowing.GetWidth() != xevent.width + || (int)g_Windowing.GetHeight() != xevent.height) + { + XBMC_Event newEvent = {0}; + newEvent.type = XBMC_VIDEORESIZE; + newEvent.resize.w = xevent.width; + newEvent.resize.h = xevent.height; + ret |= g_application.OnEvent(newEvent); + } + + /* real events have position relative to parent */ + if(!xevent.send_event) { + XWindowAttributes attr; + Window child; + XGetWindowAttributes(xevent.display, xevent.window, &attr); + XTranslateCoordinates(xevent.display + , xevent.window, attr.root + , xevent.x, xevent.x + , &xevent.x, &xevent.y + , &child); + } + + /* check for move */ + if(g_Windowing.GetLeft() != xevent.x + || g_Windowing.GetTop() != xevent.y) + { + XBMC_Event newEvent = {0}; + newEvent.type = XBMC_VIDEOMOVE; + newEvent.move.x = xevent.x; + newEvent.move.y = xevent.y; + ret |= g_application.OnEvent(newEvent); + } + return ret; +} + +bool CWinEventsX11::ProcessMotion(XMotionEvent& xmotion) +{ + XBMC_Event newEvent = {0}; + newEvent.type = XBMC_MOUSEMOTION; + newEvent.motion.xrel = (int16_t)xmotion.x_root; + newEvent.motion.yrel = (int16_t)xmotion.y_root; + newEvent.motion.x = (int16_t)xmotion.x; + newEvent.motion.y = (int16_t)xmotion.y; + return g_application.OnEvent(newEvent); +} + +bool CWinEventsX11::ProcessEnter(XCrossingEvent& xcrossing) +{ + return true; +} + +bool CWinEventsX11::ProcessLeave(XCrossingEvent& xcrossing) +{ + g_Mouse.SetActive(false); + return true; +} + +bool CWinEventsX11::ProcessButtonPress(XButtonEvent& xbutton) +{ + XBMC_Event newEvent = {0}; + newEvent.type = XBMC_MOUSEBUTTONDOWN; + newEvent.button.button = (unsigned char)(xbutton.button); + newEvent.button.state = XBMC_PRESSED; + newEvent.button.x = (int16_t)(xbutton.x); + newEvent.button.y = (int16_t)(xbutton.y); + return g_application.OnEvent(newEvent); +} + +bool CWinEventsX11::ProcessButtonRelease(XButtonEvent& xbutton) +{ + XBMC_Event newEvent = {0}; + newEvent.type = XBMC_MOUSEBUTTONUP; + newEvent.button.button = (unsigned char)(xbutton.button); + newEvent.button.state = XBMC_RELEASED; + newEvent.button.x = (int16_t)(xbutton.x); + newEvent.button.y = (int16_t)(xbutton.y); + return g_application.OnEvent(newEvent); +} + +bool CWinEventsX11::ProcessClientMessage(XClientMessageEvent& xclient) +{ + if ((Atom)(xclient.data.l[0]) == m_wmDeleteMessage) + { + if (!g_application.m_bStop) + CApplicationMessenger::Get().Quit(); + return true; + } + return false; +} + +bool CWinEventsX11::ProcessFocusIn(XFocusInEvent& xfocus) +{ + if (m_xic) + XSetICFocus(m_xic); + + g_application.m_AppFocused = true; + m_keymodState = 0; + g_Windowing.NotifyAppFocusChange(g_application.m_AppFocused); + return true; +} + +bool CWinEventsX11::ProcessFocusOut(XFocusOutEvent& xfocus) +{ + if (m_xic) + XUnsetICFocus(m_xic); + + g_application.m_AppFocused = false; + g_Windowing.NotifyAppFocusChange(g_application.m_AppFocused); + return true; +} + +bool CWinEventsX11::Process() +{ + bool ret = false; + XEvent xevent; + + while (WinEvents && XPending(m_display)) + { + memset(&xevent, 0, sizeof (XEvent)); + XNextEvent(m_display, &xevent); + + if (XFilterEvent(&xevent, None)) + continue; + + + if(!g_Windowing.IsWindowManagerControlled()) + { + switch (xevent.type) + { + case ButtonPress: + case ButtonRelease: + XSetInputFocus(m_display, m_window, RevertToParent, xevent.xbutton.time); + break; + case KeyPress: + case KeyRelease: + XSetInputFocus(m_display, m_window, RevertToParent, xevent.xkey.time); + break; + case MapNotify: + XSetInputFocus(m_display, m_window, RevertToParent, CurrentTime); + break; + } + } + + switch (xevent.type) + { + case MapNotify: + CLog::Log(LOGDEBUG,"CWinEventsX11::map %ld", xevent.xmap.window); + g_application.m_AppActive = true; + break; + + case UnmapNotify: + g_application.m_AppActive = false; + break; + + case FocusIn: + CLog::Log(LOGDEBUG,"CWinEventsX11::focus in %ld %d %d", xevent.xfocus.window, xevent.xfocus.mode, xevent.xfocus.detail); + ret |= ProcessFocusIn(xevent.xfocus); + break; + + case FocusOut: + CLog::Log(LOGDEBUG,"CWinEventsX11::focus out %ld %d %d", xevent.xfocus.window, xevent.xfocus.mode, xevent.xfocus.detail); + ret |= ProcessFocusOut(xevent.xfocus); + break; + + case Expose: + g_windowManager.MarkDirty(); + break; + + case ConfigureNotify: + ret |= ProcessConfigure(xevent.xconfigure); + break; + + case ClientMessage: + ret |= ProcessClientMessage(xevent.xclient); + break; + + case KeyPress: + ret |= ProcessKeyPress(xevent.xkey); + break; + + case KeyRelease: + ret |= ProcessKeyRelease(xevent.xkey); + break; + + case EnterNotify: + CLog::Log(LOGDEBUG,"CWinEventsX11::enter %ld %d", xevent.xcrossing.window, xevent.xcrossing.mode); + ret |= ProcessEnter(xevent.xcrossing); + break; + + case LeaveNotify: + CLog::Log(LOGDEBUG,"CWinEventsX11::leave %ld %d", xevent.xcrossing.window, xevent.xcrossing.mode); + ret |= ProcessLeave(xevent.xcrossing); + break; + + case MotionNotify: + ret |= ProcessMotion(xevent.xmotion); + break; + + case ButtonPress: + ret |= ProcessButtonPress(xevent.xbutton); + break; + + case ButtonRelease: + ret |= ProcessButtonRelease(xevent.xbutton); + break; + + default: + break; + }// switch event.type + +#if defined(HAS_XRANDR) + if (WinEvents && xevent.type == m_RREventBase + RRScreenChangeNotify) + { + XRRUpdateConfiguration(&xevent); + g_Windowing.NotifyXRREvent(); + } +#endif + + }// while + +#ifdef HAS_SDL_JOYSTICK + SDL_Event event; + while (SDL_PollEvent(&event)) + { + switch(event.type) + { + case SDL_JOYBUTTONUP: + case SDL_JOYBUTTONDOWN: + case SDL_JOYAXISMOTION: + case SDL_JOYBALLMOTION: + case SDL_JOYHATMOTION: + g_Joystick.Update(event); + ret = true; + break; + + default: + break; + } + memset(&event, 0, sizeof(SDL_Event)); + } +#endif + + return ret; +} + +bool CWinEventsX11::ProcessKey(XBMC_Event &event) +{ + if (event.type == XBMC_KEYDOWN) + { + // check key modifiers + switch(event.key.keysym.sym) + { + case XBMCK_LSHIFT: + m_keymodState |= XBMCKMOD_LSHIFT; + break; + case XBMCK_RSHIFT: + m_keymodState |= XBMCKMOD_RSHIFT; + break; + case XBMCK_LCTRL: + m_keymodState |= XBMCKMOD_LCTRL; + break; + case XBMCK_RCTRL: + m_keymodState |= XBMCKMOD_RCTRL; + break; + case XBMCK_LALT: + m_keymodState |= XBMCKMOD_LALT; + break; + case XBMCK_RALT: + m_keymodState |= XBMCKMOD_RCTRL; + break; + case XBMCK_LMETA: + m_keymodState |= XBMCKMOD_LMETA; + break; + case XBMCK_RMETA: + m_keymodState |= XBMCKMOD_RMETA; + break; + case XBMCK_MODE: + m_keymodState |= XBMCKMOD_MODE; + break; + default: + break; + } + event.key.keysym.mod = (XBMCMod)m_keymodState; + + bool ret = ProcessShortcuts(event); + if (ret) + return ret; + } + else if (event.type == XBMC_KEYUP) + { + switch(event.key.keysym.sym) + { + case XBMCK_LSHIFT: + m_keymodState &= ~XBMCKMOD_LSHIFT; + break; + case XBMCK_RSHIFT: + m_keymodState &= ~XBMCKMOD_RSHIFT; + break; + case XBMCK_LCTRL: + m_keymodState &= ~XBMCKMOD_LCTRL; + break; + case XBMCK_RCTRL: + m_keymodState &= ~XBMCKMOD_RCTRL; + break; + case XBMCK_LALT: + m_keymodState &= ~XBMCKMOD_LALT; + break; + case XBMCK_RALT: + m_keymodState &= ~XBMCKMOD_RCTRL; + break; + case XBMCK_LMETA: + m_keymodState &= ~XBMCKMOD_LMETA; + break; + case XBMCK_RMETA: + m_keymodState &= ~XBMCKMOD_RMETA; + break; + case XBMCK_MODE: + m_keymodState &= ~XBMCKMOD_MODE; + break; + default: + break; + } + event.key.keysym.mod = (XBMCMod)m_keymodState; + } + + return g_application.OnEvent(event); +} + +bool CWinEventsX11::ProcessShortcuts(XBMC_Event& event) +{ + if (event.key.keysym.mod & XBMCKMOD_ALT) + { + switch(event.key.keysym.sym) + { + case XBMCK_TAB: // ALT+TAB to minimize/hide + g_application.Minimize(); + return true; + + default: + return false; + } + } + return false; +} + +XBMCKey CWinEventsX11::LookupXbmcKeySym(KeySym keysym) +{ + // try direct mapping first + std::map::iterator it; + it = m_symLookupTable.find(keysym); + if (it != m_symLookupTable.end()) + { + return (XBMCKey)(it->second); + } + + // try ascii mappings + if (keysym>>8 == 0x00) + return (XBMCKey)(keysym & 0xFF); + + return (XBMCKey)keysym; +} +#endif diff --git a/xbmc/windowing/WinEventsX11.h b/xbmc/windowing/WinEventsX11.h new file mode 100644 index 0000000000000..d553019963130 --- /dev/null +++ b/xbmc/windowing/WinEventsX11.h @@ -0,0 +1,67 @@ +/* +* Copyright (C) 2005-2012 Team XBMC +* http://www.xbmc.org +* +* This Program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2, or (at your option) +* any later version. +* +* This Program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with XBMC; see the file COPYING. If not, write to +* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +* http://www.gnu.org/copyleft/gpl.html +* +*/ +#pragma once + +#include "WinEvents.h" +#include +#include +#include "threads/SystemClock.h" +#include + +class CWinEventsX11 : public CWinEventsBase +{ +public: + virtual ~CWinEventsX11(); + static bool Init(Display *dpy, Window win); + static void Quit(); + static bool MessagePump(); + +protected: + CWinEventsX11(Display *dpy, Window win); + XBMCKey LookupXbmcKeySym(KeySym keysym); + bool Process(); + bool ProcessMotion (XMotionEvent& xmotion); + bool ProcessConfigure (XConfigureEvent& xevent); + bool ProcessKeyPress (XKeyEvent& xevent); + bool ProcessKeyRelease (XKeyEvent& xevent); + bool ProcessButtonPress (XButtonEvent& xbutton); + bool ProcessButtonRelease(XButtonEvent& xbutton); + bool ProcessClientMessage(XClientMessageEvent& xclient); + bool ProcessFocusIn (XFocusInEvent& xfocus); + bool ProcessFocusOut (XFocusOutEvent& xfocus); + bool ProcessEnter (XCrossingEvent& xcrossing); + bool ProcessLeave (XCrossingEvent& xcrossing); + bool ProcessKey (XBMC_Event &event); + bool ProcessKeyRepeat(); + bool ProcessShortcuts(XBMC_Event& event); + static CWinEventsX11 *WinEvents; + Display *m_display; + Window m_window; + Atom m_wmDeleteMessage; + char *m_keybuf; + size_t m_keybuf_len; + XIM m_xim; + XIC m_xic; + XComposeStatus m_compose; + std::map m_symLookupTable; + int m_keymodState; + int m_RREventBase; +}; diff --git a/xbmc/windowing/WinSystem.cpp b/xbmc/windowing/WinSystem.cpp index d066b89dcecaa..df9ebed1e9885 100644 --- a/xbmc/windowing/WinSystem.cpp +++ b/xbmc/windowing/WinSystem.cpp @@ -32,7 +32,6 @@ CWinSystemBase::CWinSystemBase() m_nLeft = 0; m_bWindowCreated = false; m_bFullScreen = false; - m_nScreen = 0; m_bBlankOtherDisplay = false; m_fRefreshRate = 0.0f; } diff --git a/xbmc/windowing/WinSystem.h b/xbmc/windowing/WinSystem.h index 514242ddaf7cc..f23724dbc2580 100644 --- a/xbmc/windowing/WinSystem.h +++ b/xbmc/windowing/WinSystem.h @@ -91,6 +91,9 @@ class CWinSystemBase // resolution interfaces unsigned int GetWidth() { return m_nWidth; } unsigned int GetHeight() { return m_nHeight; } + int GetLeft() { return m_nLeft; } + int GetTop() { return m_nTop; } + virtual int GetNumScreens() { return 0; } virtual int GetCurrentScreen() { return 0; } bool IsFullScreen() { return m_bFullScreen; } @@ -111,7 +114,6 @@ class CWinSystemBase int m_nLeft; bool m_bWindowCreated; bool m_bFullScreen; - int m_nScreen; bool m_bBlankOtherDisplay; float m_fRefreshRate; }; diff --git a/xbmc/windowing/X11/WinSystemX11.cpp b/xbmc/windowing/X11/WinSystemX11.cpp index 889c445a2fa03..86c298f75c3a0 100644 --- a/xbmc/windowing/X11/WinSystemX11.cpp +++ b/xbmc/windowing/X11/WinSystemX11.cpp @@ -22,36 +22,66 @@ #ifdef HAS_GLX -#include #include "WinSystemX11.h" #include "settings/Settings.h" -#include "guilib/Texture.h" +#include "pictures/DllImageLib.h" #include "guilib/DispResource.h" #include "utils/log.h" #include "XRandR.h" #include #include "threads/SingleLock.h" #include +#include #include "cores/VideoRenderers/RenderManager.h" #include "utils/TimeUtils.h" +#include "Application.h" #if defined(HAS_XRANDR) #include #endif +#include "../WinEvents.h" + using namespace std; +static bool SetOutputMode(RESOLUTION_INFO& res) +{ +#if defined(HAS_XRANDR) + XOutput out; + XMode mode = g_xrandr.GetCurrentMode(res.iInternal, res.strOutput); + + /* mode matches so we are done */ + if(res.strId == mode.id) + return false; + + /* set up mode */ + out.name = res.strOutput; + out.screen = res.iInternal; + mode.w = res.iWidth; + mode.h = res.iHeight; + mode.hz = res.fRefreshRate; + mode.id = res.strId; + g_xrandr.SetMode(out, mode); + return true; +#endif +} + CWinSystemX11::CWinSystemX11() : CWinSystemBase() { m_eWindowSystem = WINDOW_SYSTEM_X11; m_glContext = NULL; - m_SDLSurface = NULL; m_dpy = NULL; + m_visual = NULL; m_glWindow = 0; m_wmWindow = 0; m_bWasFullScreenBeforeMinimize = false; m_minimized = false; m_dpyLostTime = 0; + m_invisibleCursor = 0; + m_outputName = ""; + m_outputIndex = 0; + m_wm_fullscreen = false; + m_wm_controlled = false; XSetErrorHandler(XErrorHandler); } @@ -64,19 +94,11 @@ bool CWinSystemX11::InitWindowSystem() { if ((m_dpy = XOpenDisplay(NULL))) { + if(!CWinSystemBase::InitWindowSystem()) + return false; - SDL_EnableUNICODE(1); - // set repeat to 10ms to ensure repeat time < frame time - // so that hold times can be reliably detected - SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, 10); - - SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); - SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); - SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); - SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8); - SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); - - return CWinSystemBase::InitWindowSystem(); + UpdateResolutions(); + return true; } else CLog::Log(LOGERROR, "GLX Error: No Display found"); @@ -86,163 +108,595 @@ bool CWinSystemX11::InitWindowSystem() bool CWinSystemX11::DestroyWindowSystem() { -#if defined(HAS_XRANDR) //restore videomode on exit if (m_bFullScreen) - g_xrandr.RestoreState(); -#endif + SetOutputMode(g_settings.m_ResInfo[DesktopResolution(m_outputIndex)]); + + if (m_visual) + { + XFree(m_visual); + m_visual = NULL; + } if (m_dpy) { - if (m_glContext) + XCloseDisplay(m_dpy); + m_dpy = NULL; + } + + return true; +} + +/** + * @brief Allocate a cursor that won't be visible on the window + */ +static Cursor AllocateInvisibleCursor(Display* dpy, Window wnd) +{ + Pixmap bitmap; + XColor black = {0}; + Cursor cursor; + static char data[] = { 0,0,0,0,0,0,0,0 }; + + bitmap = XCreateBitmapFromData(dpy, wnd, data, 8, 8); + cursor = XCreatePixmapCursor(dpy, bitmap, bitmap, + &black, &black, 0, 0); + XFreePixmap(dpy, bitmap); + return cursor; +} + +static Pixmap AllocateIconPixmap(Display* dpy, Window w) +{ + int depth; + XImage *img = NULL; + Visual *vis; + XWindowAttributes wndattribs; + XVisualInfo visInfo; + double rRatio; + double gRatio; + double bRatio; + int outIndex = 0; + unsigned i,j; + unsigned char *buf; + uint32_t *newBuf = 0; + size_t numNewBufBytes; + + // Get visual Info + XGetWindowAttributes(dpy, w, &wndattribs); + visInfo.visualid = wndattribs.visual->visualid; + int nvisuals = 0; + XVisualInfo* visuals = XGetVisualInfo(dpy, VisualIDMask, &visInfo, &nvisuals); + if (nvisuals != 1) + { + CLog::Log(LOGERROR, "CWinSystemX11::CreateIconPixmap - could not find visual"); + return None; + } + visInfo = visuals[0]; + XFree(visuals); + + depth = visInfo.depth; + vis = visInfo.visual; + + if (depth < 15) + { + CLog::Log(LOGERROR, "CWinSystemX11::CreateIconPixmap - no suitable depth"); + return None; + } + + rRatio = vis->red_mask / 255.0; + gRatio = vis->green_mask / 255.0; + bRatio = vis->blue_mask / 255.0; + + DllImageLib dll; + if (!dll.Load()) + return false; + + ImageInfo image; + memset(&image, 0, sizeof(image)); + + if(!dll.LoadImage("special://xbmc/media/icon.png", 256, 256, &image)) + { + CLog::Log(LOGERROR, "AllocateIconPixmap to load icon"); + return false; + } + + buf = image.texture; + int pitch = ((image.width + 1)* 3 / 4) * 4; + + + if (depth>=24) + numNewBufBytes = 4 * image.width * image.height; + else + numNewBufBytes = 2 * image.width * image.height; + + newBuf = (uint32_t*)malloc(numNewBufBytes); + if (!newBuf) + { + CLog::Log(LOGERROR, "CWinSystemX11::CreateIconPixmap - malloc failed"); + dll.ReleaseImage(&image); + return None; + } + + for (i=0; ired_mask; + g &= vis->green_mask; + b &= vis->blue_mask; + newBuf[outIndex] = r | g | b; + ++outIndex; } + } - m_glContext = 0; + dll.ReleaseImage(&image); - //we don't call XCloseDisplay() here, since ati keeps a pointer to our m_dpy - //so instead we just let m_dpy die on exit + img = XCreateImage(dpy, vis, depth,ZPixmap, 0, (char *)newBuf, + image.width, image.height, + (depth>=24)?32:16, 0); + if (!img) + { + CLog::Log(LOGERROR, "CWinSystemX11::CreateIconPixmap - could not create image"); + free(newBuf); + return None; + } + if (!XInitImage(img)) + { + CLog::Log(LOGERROR, "CWinSystemX11::CreateIconPixmap - init image failed"); + XDestroyImage(img); + return None; } - // m_SDLSurface is free()'d by SDL_Quit(). + // set byte order + union + { + char c[sizeof(short)]; + short s; + } order; + order.s = 1; + if ((1 == order.c[0])) + { + img->byte_order = LSBFirst; + } + else + { + img->byte_order = MSBFirst; + } - return true; + // create icon pixmap from image + Pixmap icon = XCreatePixmap(dpy, w, img->width, img->height, depth); + GC gc = XCreateGC(dpy, w, 0, NULL); + XPutImage(dpy, icon, gc, img, 0, 0, 0, 0, img->width, img->height); + XFreeGC(dpy, gc); + XDestroyImage(img); // this also frees newBuf + + return icon; } -bool CWinSystemX11::CreateNewWindow(const CStdString& name, bool fullScreen, RESOLUTION_INFO& res, PHANDLE_EVENT_FUNC userFunction) +void CWinSystemX11::ProbeWindowManager() { - RESOLUTION_INFO& desktop = g_settings.m_ResInfo[RES_DESKTOP]; + int res, format; + Atom *items, type; + unsigned long bytes_after, nitems; + unsigned char *prop_return; + int wm_window_id; + + m_wm = false; + m_wm_name = ""; + m_wm_fullscreen = false; + + res = XGetWindowProperty(m_dpy, XRootWindow(m_dpy, m_visual->screen), m_NET_SUPPORTING_WM_CHECK, + 0, 16384, False, AnyPropertyType, &type, &format, + &nitems, &bytes_after, &prop_return); + if(res != Success || nitems == 0 || prop_return == NULL) + { + CLog::Log(LOGDEBUG, "CWinSystemX11::ProbeWindowManager - No window manager found (NET_SUPPORTING_WM_CHECK not set)"); + return; + } - if (fullScreen && - (res.iWidth != desktop.iWidth || res.iHeight != desktop.iHeight || - res.fRefreshRate != desktop.fRefreshRate || res.iScreen != desktop.iScreen)) + wm_window_id = *(int *)prop_return; + XFree(prop_return); + prop_return = NULL; + + res = XGetWindowProperty(m_dpy, wm_window_id, m_NET_WM_NAME, + 0, 16384, False, AnyPropertyType, &type, &format, + &nitems, &bytes_after, &prop_return); + if(res != Success || nitems == 0 || prop_return == NULL) + { + CLog::Log(LOGDEBUG, "CWinSystemX11::ProbeWindowManager - No window manager found (NET_WM_NAME not set on wm window)"); + return; + } + m_wm_name = (char *)prop_return; + XFree(prop_return); + prop_return = NULL; + + res = XGetWindowProperty(m_dpy, XRootWindow(m_dpy, m_visual->screen), m_NET_SUPPORTED, + 0, 16384, False, AnyPropertyType, &type, &format, + &nitems, &bytes_after, &prop_return); + if(res != Success || nitems == 0 || prop_return == NULL) { - //on the first call to SDL_SetVideoMode, SDL stores the current displaymode - //SDL restores the displaymode on SDL_QUIT(), if we change the displaymode - //before the first call to SDL_SetVideoMode, SDL changes the displaymode back - //to the wrong mode on exit + CLog::Log(LOGDEBUG, "CWinSystemX11::ProbeWindowManager - No window manager found (NET_SUPPORTING_WM_CHECK not set)"); + return; + } + items = (Atom *)prop_return; + m_wm = true; + for(unsigned long i = 0; i < nitems; i++) + { + if(items[i] == m_NET_WM_STATE_FULLSCREEN) + m_wm_fullscreen = true; + } + XFree(prop_return); + prop_return = NULL; - CLog::Log(LOGINFO, "CWinSystemX11::CreateNewWindow initializing to desktop resolution first"); - if (!SetFullScreen(true, desktop, false)) - return false; + CLog::Log(LOGDEBUG, "CWinSystemX11::ProbeWindowManager - Window manager (%s) detected%s", + m_wm_name.c_str(), + m_wm_fullscreen ? ", can fullscreen" : ""); + + if(m_wm_fullscreen && g_application.IsStandAlone()) + { + CLog::Log(LOGDEBUG, "CWinSystemX11::ProbeWindowManager - Disable window manager fullscreen due to standalone"); + m_wm_fullscreen = false; + } +} + + +bool CWinSystemX11::CreateNewWindow(const CStdString& name, bool fullScreen, RESOLUTION_INFO& res, PHANDLE_EVENT_FUNC userFunction) +{ + int x = 0 + , y = 0 + , w = res.iWidth + , h = res.iHeight; + + if (m_wmWindow) + { + OnLostDevice(); + DestroyWindow(); } - if(!SetFullScreen(fullScreen, res, false)) + /* update available atoms */ + m_NET_SUPPORTING_WM_CHECK = XInternAtom(m_dpy, "_NET_SUPPORTING_WM_CHECK", False); + m_NET_WM_STATE = XInternAtom(m_dpy, "_NET_WM_STATE", False); + m_NET_WM_STATE_FULLSCREEN = XInternAtom(m_dpy, "_NET_WM_STATE_FULLSCREEN", False); + m_NET_WM_STATE_MAXIMIZED_VERT = XInternAtom(m_dpy, "_NET_WM_STATE_MAXIMIZED_VERT", False); + m_NET_WM_STATE_MAXIMIZED_HORZ = XInternAtom(m_dpy, "_NET_WM_STATE_MAXIMIZED_HORZ", False); + m_NET_SUPPORTED = XInternAtom(m_dpy, "_NET_SUPPORTED", False); + m_NET_WM_STATE_FULLSCREEN = XInternAtom(m_dpy, "_NET_WM_STATE_FULLSCREEN", False); + m_NET_SUPPORTING_WM_CHECK = XInternAtom(m_dpy, "_NET_SUPPORTING_WM_CHECK", False); + m_NET_WM_NAME = XInternAtom(m_dpy, "_NET_WM_NAME", False); + m_WM_DELETE_WINDOW = XInternAtom(m_dpy, "WM_DELETE_WINDOW", False); + + /* higher layer should have set m_visual */ + if(m_visual == NULL) + { + CLog::Log(LOGERROR, "CWinSystemX11::CreateNewWindow - no visual setup"); return false; + } + + /* figure out what the window manager support */ + ProbeWindowManager(); - CBaseTexture* iconTexture = CTexture::LoadFromFile("special://xbmc/media/icon.png"); + /* make sure our requsted output has the right resolution */ + SetResolution(res, fullScreen); - if (iconTexture) - SDL_WM_SetIcon(SDL_CreateRGBSurfaceFrom(iconTexture->GetPixels(), iconTexture->GetWidth(), iconTexture->GetHeight(), 32, iconTexture->GetPitch(), 0xff0000, 0x00ff00, 0x0000ff, 0xff000000L), NULL); - SDL_WM_SetCaption("XBMC Media Center", NULL); - delete iconTexture; + XSetWindowAttributes swa = {0}; - // register XRandR Events #if defined(HAS_XRANDR) - int iReturn; - XRRQueryExtension(m_dpy, &m_RREventBase, &iReturn); - XRRSelectInput(m_dpy, m_wmWindow, RRScreenChangeNotifyMask); + XOutput out = g_xrandr.GetOutput(m_visual->screen, res.strOutput); + if(out.isConnected) + { + x = out.x; + y = out.y; + } #endif + if(m_wm_fullscreen || fullScreen == false) + { + /* center in display */ + RESOLUTION_INFO& win = g_settings.m_ResInfo[RES_WINDOW]; + w = win.iWidth; + h = win.iHeight; + x += res.iWidth / 2 - w / 2; + y += res.iHeight / 2 - w / 2; + + swa.override_redirect = 0; + swa.border_pixel = 5; + } + else + { + swa.override_redirect = 1; + swa.border_pixel = 0; + } + + if(m_visual->visual == DefaultVisual(m_dpy, m_visual->screen)) + swa.background_pixel = BlackPixel(m_dpy, m_visual->screen); + + swa.colormap = XCreateColormap(m_dpy, RootWindow(m_dpy, m_visual->screen), m_visual->visual, AllocNone); + swa.event_mask = FocusChangeMask | KeyPressMask | KeyReleaseMask | + ButtonPressMask | ButtonReleaseMask | PointerMotionMask | + PropertyChangeMask | StructureNotifyMask | KeymapStateMask | + EnterWindowMask | LeaveWindowMask | ExposureMask; + + m_wmWindow = XCreateWindow(m_dpy, RootWindow(m_dpy, m_visual->screen), + x, y, + w, h, + 0, m_visual->depth, + InputOutput, m_visual->visual, + CWBackPixel | CWBorderPixel | CWColormap | CWOverrideRedirect | CWEventMask, + &swa); + + if(m_wm_fullscreen && fullScreen) + XChangeProperty(m_dpy, m_wmWindow, m_NET_WM_STATE, XA_ATOM, 32, PropModeReplace, (unsigned char *) &m_NET_WM_STATE_FULLSCREEN, 1); + + m_invisibleCursor = AllocateInvisibleCursor(m_dpy, m_wmWindow); + XDefineCursor(m_dpy, m_wmWindow, m_invisibleCursor); + + m_icon = AllocateIconPixmap(m_dpy, m_wmWindow); + + XWMHints* wm_hints = XAllocWMHints(); + XTextProperty windowName, iconName; + const char* title = "XBMC Media Center"; + + XStringListToTextProperty((char**)&title, 1, &windowName); + XStringListToTextProperty((char**)&title, 1, &iconName); + wm_hints->initial_state = NormalState; + wm_hints->input = True; + wm_hints->icon_pixmap = m_icon; + wm_hints->flags = StateHint | IconPixmapHint | InputHint; + + XSetWMProperties(m_dpy, m_wmWindow, &windowName, &iconName, + NULL, 0, NULL, wm_hints, + NULL); + + XFree(wm_hints); + + // register interest in the delete window message + XSetWMProtocols(m_dpy, m_wmWindow, &m_WM_DELETE_WINDOW, 1); + + XMapRaised(m_dpy, m_wmWindow); + XSync(m_dpy, True); + + RefreshWindowState(); + + //init X11 events + CWinEvents::Init(m_dpy, m_wmWindow); + m_bWindowCreated = true; return true; } bool CWinSystemX11::DestroyWindow() { + if (m_glContext) + { + glFinish(); + glXMakeCurrent(m_dpy, None, NULL); + } + + if (m_invisibleCursor) + { + XUndefineCursor(m_dpy, m_wmWindow); + XFreeCursor(m_dpy, m_invisibleCursor); + m_invisibleCursor = 0; + } + + CWinEvents::Quit(); + + if(m_wmWindow) + { + XUnmapWindow(m_dpy, m_wmWindow); + XSync(m_dpy, True); + XDestroyWindow(m_dpy, m_wmWindow); + m_wmWindow = 0; + } + + if (m_icon) + { + XFreePixmap(m_dpy, m_icon); + m_icon = None; + } + return true; } bool CWinSystemX11::ResizeWindow(int newWidth, int newHeight, int newLeft, int newTop) { - if(m_nWidth == newWidth - && m_nHeight == newHeight) - return true; + RefreshWindowState(); - m_nWidth = newWidth; - m_nHeight = newHeight; + if(newLeft < 0) newLeft = m_nLeft; + if(newTop < 0) newTop = m_nTop; - int options = SDL_OPENGL; - if (m_bFullScreen) - options |= SDL_FULLSCREEN; - else - options |= SDL_RESIZABLE; - - if ((m_SDLSurface = SDL_SetVideoMode(m_nWidth, m_nHeight, 0, options))) + /* check if we are already correct */ + if(m_nWidth != newWidth + && m_nHeight != newHeight + && m_nLeft != newLeft + && m_nTop != newTop) { - RefreshGlxContext(); - return true; + XMoveResizeWindow(m_dpy, m_wmWindow, newLeft, newTop, newWidth, newHeight); + XSync(m_dpy, False); + RefreshWindowState(); } - return false; + return true; +} + +bool CWinSystemX11::SetResolution(RESOLUTION_INFO& res, bool fullScreen) +{ + bool changed = false; + /* if we switched outputs or went to desktop, restore old resolution */ + if(m_bFullScreen && (m_outputName != res.strOutput || !fullScreen)) + changed |= SetOutputMode(g_settings.m_ResInfo[DesktopResolution(m_outputIndex)]); + + /* setup wanted mode on wanted display */ + if(fullScreen) + changed |= SetOutputMode(res); + + if(changed) + { + CLog::Log(LOGNOTICE, "CWinSystemX11::SetResolution - modes changed, reset device"); + OnLostDevice(); + XSync(m_dpy, False); + } + return changed; } bool CWinSystemX11::SetFullScreen(bool fullScreen, RESOLUTION_INFO& res, bool blankOtherDisplays) { - m_nWidth = res.iWidth; - m_nHeight = res.iHeight; - m_bFullScreen = fullScreen; + if(res.iInternal != m_visual->screen) + { + CreateNewWindow("", fullScreen, res, NULL); + m_bFullScreen = fullScreen; + m_outputName = res.strOutput; + m_outputIndex = res.iScreen; + return true; + } + + SetResolution(res, fullScreen); + int x = 0, y = 0; #if defined(HAS_XRANDR) - XOutput out; - XMode mode; - out.name = res.strOutput; - mode.w = res.iWidth; - mode.h = res.iHeight; - mode.hz = res.fRefreshRate; - mode.id = res.strId; - - if(m_bFullScreen) + XOutput out = g_xrandr.GetOutput(res.iInternal, res.strOutput); + if(out.isConnected) { - OnLostDevice(); - g_xrandr.SetMode(out, mode); + x = out.x; + y = out.y; } - else - g_xrandr.RestoreState(); #endif - int options = SDL_OPENGL; - if (m_bFullScreen) - options |= SDL_FULLSCREEN; - else - options |= SDL_RESIZABLE; - if ((m_SDLSurface = SDL_SetVideoMode(m_nWidth, m_nHeight, 0, options))) + if(!fullScreen) { - if ((m_SDLSurface->flags & SDL_OPENGL) != SDL_OPENGL) - CLog::Log(LOGERROR, "CWinSystemX11::SetFullScreen SDL_OPENGL not set, SDL_GetError:%s", SDL_GetError()); + x = m_nLeft; + y = m_nTop; + } - RefreshGlxContext(); + XWindowAttributes attr2; + XGetWindowAttributes(m_dpy, m_wmWindow, &attr2); + + if(m_wm_fullscreen) + { + /* if on other screen, we must move it first, otherwise + * the window manager will fullscreen it on the wrong + * window */ + if(fullScreen && GetCurrentScreen() != res.iScreen) + XMoveWindow(m_dpy, m_wmWindow, x, y); + + if(attr2.map_state == IsUnmapped) + { + if(fullScreen) + XChangeProperty(m_dpy, m_wmWindow, m_NET_WM_STATE, XA_ATOM, 32, PropModeReplace, (unsigned char *) &m_NET_WM_STATE_FULLSCREEN, 1); + else + XDeleteProperty(m_dpy, m_wmWindow, m_NET_WM_STATE); + } + else + { + XEvent e = {0}; + e.xany.type = ClientMessage; + e.xclient.message_type = m_NET_WM_STATE; + e.xclient.display = m_dpy; + e.xclient.window = m_wmWindow; + e.xclient.format = 32; + if(fullScreen) + { + e.xclient.data.l[0] = 1; /* _NET_WM_STATE_ADD */ + e.xclient.data.l[1] = m_NET_WM_STATE_FULLSCREEN; + } + else + { + e.xclient.data.l[0] = 0; /* _NET_WM_STATE_REMOVE */ + e.xclient.data.l[1] = m_NET_WM_STATE_FULLSCREEN; + } + + XSendEvent(m_dpy, RootWindow(m_dpy, m_visual->screen), False, + SubstructureNotifyMask | SubstructureRedirectMask, &e); + } - return true; } + else + { + XSetWindowAttributes attr = {0}; + if(fullScreen) + attr.override_redirect = 1; + else + attr.override_redirect = 0; - return false; + /* if override_redirect changes, we need to notify + * WM by reparenting the window to root */ + if(attr.override_redirect != attr2.override_redirect) + { + XUnmapWindow (m_dpy, m_wmWindow); + XChangeWindowAttributes(m_dpy, m_wmWindow, CWOverrideRedirect, &attr); + XReparentWindow (m_dpy, m_wmWindow, RootWindow(m_dpy, res.iInternal), x, y); + XGetWindowAttributes (m_dpy, m_wmWindow, &attr2); + } + + XWindowChanges cw; + cw.x = x; + cw.y = y; + if(fullScreen) + cw.border_width = 0; + else + cw.border_width = 5; + cw.width = res.iWidth; + cw.height = res.iHeight; + + XConfigureWindow(m_dpy, m_wmWindow, CWX | CWY | CWWidth | CWHeight | CWBorderWidth, &cw); + } + + XMapRaised(m_dpy, m_wmWindow); + XSync(m_dpy, False); + + m_bFullScreen = fullScreen; + m_outputName = res.strOutput; + m_outputIndex = res.iScreen; + + RefreshWindowState(); + return true; } void CWinSystemX11::UpdateResolutions() { CWinSystemBase::UpdateResolutions(); + // erase previous stored modes + if (g_settings.m_ResInfo.size() > (unsigned)RES_DESKTOP) + { + g_settings.m_ResInfo.erase(g_settings.m_ResInfo.begin()+RES_DESKTOP + , g_settings.m_ResInfo.end()); + } #if defined(HAS_XRANDR) - if(g_xrandr.Query()) + // add desktop modes + g_xrandr.Query(XScreenCount(m_dpy), true); + std::vector outs = g_xrandr.GetModes(); + if(outs.size() > 0) { - XOutput out = g_xrandr.GetCurrentOutput(); - XMode mode = g_xrandr.GetCurrentMode(out.name); - UpdateDesktopResolution(g_settings.m_ResInfo[RES_DESKTOP], 0, mode.w, mode.h, mode.hz); - g_settings.m_ResInfo[RES_DESKTOP].strId = mode.id; - g_settings.m_ResInfo[RES_DESKTOP].strOutput = out.name; + for(unsigned i = 0; i < outs.size(); ++i) + { + XOutput out = outs[i]; + XMode mode = g_xrandr.GetCurrentMode(out.screen, out.name); + RESOLUTION_INFO res; + + UpdateDesktopResolution(res, i, mode.w, mode.h, mode.hz); + res.strId = mode.id; + res.strOutput = out.name; + res.iInternal = out.screen; + g_settings.m_ResInfo.push_back(res); + } } else #endif { + RESOLUTION_INFO res; int x11screen = DefaultScreen(m_dpy); int w = DisplayWidth(m_dpy, x11screen); int h = DisplayHeight(m_dpy, x11screen); - UpdateDesktopResolution(g_settings.m_ResInfo[RES_DESKTOP], 0, w, h, 0.0); + res.iInternal = x11screen; + UpdateDesktopResolution(res, 0, w, h, 0.0); + g_settings.m_ResInfo.push_back(res); } @@ -250,8 +704,6 @@ void CWinSystemX11::UpdateResolutions() CLog::Log(LOGINFO, "Available videomodes (xrandr):"); vector::iterator outiter; - vector outs; - outs = g_xrandr.GetModes(); CLog::Log(LOGINFO, "Number of connected outputs: %"PRIdS"", outs.size()); string modename = ""; @@ -279,6 +731,8 @@ void CWinSystemX11::UpdateResolutions() res.strMode.Format("%s: %s @ %.2fHz", out.name.c_str(), mode.name.c_str(), mode.hz); res.strOutput = out.name; res.strId = mode.id; + res.iScreen = outiter - outs.begin(); + res.iInternal = out.screen; res.iSubtitles = (int)(0.95*mode.h); res.fRefreshRate = mode.hz; res.bFullScreen = true; @@ -296,124 +750,69 @@ void CWinSystemX11::UpdateResolutions() } -bool CWinSystemX11::IsSuitableVisual(XVisualInfo *vInfo) +int CWinSystemX11::GetNumScreens() { - int value; - if (glXGetConfig(m_dpy, vInfo, GLX_RGBA, &value) || !value) - return false; - if (glXGetConfig(m_dpy, vInfo, GLX_DOUBLEBUFFER, &value) || !value) - return false; - if (glXGetConfig(m_dpy, vInfo, GLX_RED_SIZE, &value) || value < 8) - return false; - if (glXGetConfig(m_dpy, vInfo, GLX_GREEN_SIZE, &value) || value < 8) - return false; - if (glXGetConfig(m_dpy, vInfo, GLX_BLUE_SIZE, &value) || value < 8) - return false; - if (glXGetConfig(m_dpy, vInfo, GLX_ALPHA_SIZE, &value) || value < 8) - return false; - if (glXGetConfig(m_dpy, vInfo, GLX_DEPTH_SIZE, &value) || value < 8) - return false; - return true; +#if defined(HAS_XRANDR) + int count = g_xrandr.GetModes().size(); + if(count > 0) + return count; + else + return 0; +#else + return 1; +#endif } -bool CWinSystemX11::RefreshGlxContext() +int CWinSystemX11::GetCurrentScreen() { - bool retVal = false; - SDL_SysWMinfo info; - SDL_VERSION(&info.version); - if (SDL_GetWMInfo(&info) <= 0) - { - CLog::Log(LOGERROR, "Failed to get window manager info from SDL"); - return false; - } - if(m_glWindow == info.info.x11.window && m_glContext) +#if defined(HAS_XRANDR) + std::vector outs = g_xrandr.GetModes(); + int best_index = -1; + int best_overlap = 0; + for(unsigned i = 0; i < outs.size(); ++i) { - CLog::Log(LOGERROR, "GLX: Same window as before, refreshing context"); - glXMakeCurrent(m_dpy, None, NULL); - glXMakeCurrent(m_dpy, m_glWindow, m_glContext); - return true; + XOutput out = outs[i]; + if(out.screen != m_visual->screen) + continue; + + int w = std::max(0, std::min(m_nLeft + m_nWidth , out.x + out.w) - std::max(m_nLeft, out.x)); + int h = std::max(0, std::min(m_nTop + m_nHeight, out.y + out.h) - std::max(m_nTop , out.y)); + if(w*h > best_overlap) + best_index = i; } - XVisualInfo vMask; - XVisualInfo *visuals; - XVisualInfo *vInfo = NULL; - int availableVisuals = 0; - vMask.screen = DefaultScreen(m_dpy); - XWindowAttributes winAttr; - m_glWindow = info.info.x11.window; - m_wmWindow = info.info.x11.wmwindow; - - /* Assume a depth of 24 in case the below calls to XGetWindowAttributes() - or XGetVisualInfo() fail. That shouldn't happen unless something is - fatally wrong, but lets prepare for everything. */ - vMask.depth = 24; + if(best_index >= 0) + return best_index; +#endif - if (XGetWindowAttributes(m_dpy, m_glWindow, &winAttr)) - { - vMask.visualid = XVisualIDFromVisual(winAttr.visual); - vInfo = XGetVisualInfo(m_dpy, VisualScreenMask | VisualIDMask, &vMask, &availableVisuals); - if (!vInfo) - CLog::Log(LOGWARNING, "Failed to get VisualInfo of SDL visual 0x%x", (unsigned) vMask.visualid); - else if(!IsSuitableVisual(vInfo)) - { - CLog::Log(LOGWARNING, "Visual 0x%x of the SDL window is not suitable, looking for another one...", - (unsigned) vInfo->visualid); - vMask.depth = vInfo->depth; - XFree(vInfo); - vInfo = NULL; - } - } - else - CLog::Log(LOGWARNING, "Failed to get SDL window attributes"); + return m_outputIndex; +} - /* As per glXMakeCurrent documentation, we have to use the same visual as - m_glWindow. Since that was not suitable for use, we try to use another - one with the same depth and hope that the used implementation is less - strict than the documentation. */ - if (!vInfo) +void CWinSystemX11::RefreshWindowState() +{ + XWindowAttributes attr; + Window child; + XGetWindowAttributes(m_dpy, m_wmWindow, &attr); + XTranslateCoordinates(m_dpy, m_wmWindow, attr.root, -attr.border_width, -attr.border_width, &attr.x, &attr.y, &child); + + m_nWidth = attr.width; + m_nHeight = attr.height; + if(!m_bFullScreen) { - visuals = XGetVisualInfo(m_dpy, VisualScreenMask | VisualDepthMask, &vMask, &availableVisuals); - for (int i = 0; i < availableVisuals; i++) - { - if (IsSuitableVisual(&visuals[i])) - { - vMask.visualid = visuals[i].visualid; - vInfo = XGetVisualInfo(m_dpy, VisualScreenMask | VisualIDMask, &vMask, &availableVisuals); - break; - } - } - XFree(visuals); + m_nLeft = attr.x; + m_nTop = attr.y; } - if (vInfo) - { - CLog::Log(LOGNOTICE, "Using visual 0x%x", (unsigned) vInfo->visualid); - if (m_glContext) - { - glXMakeCurrent(m_dpy, None, NULL); - glXDestroyContext(m_dpy, m_glContext); - } - - if ((m_glContext = glXCreateContext(m_dpy, vInfo, NULL, True))) - { - // make this context current - glXMakeCurrent(m_dpy, m_glWindow, m_glContext); - retVal = true; - } - else - CLog::Log(LOGERROR, "GLX Error: Could not create context"); - XFree(vInfo); - } - else - CLog::Log(LOGERROR, "GLX Error: vInfo is NULL!"); - - return retVal; + m_wm_controlled = attr.override_redirect == 0 && m_wm_name; } void CWinSystemX11::ShowOSMouse(bool show) { - SDL_ShowCursor(show ? 1 : 0); + if (show) + XUndefineCursor(m_dpy,m_wmWindow); + else if (m_invisibleCursor) + XDefineCursor(m_dpy,m_wmWindow, m_invisibleCursor); } void CWinSystemX11::ResetOSScreensaver() @@ -427,8 +826,6 @@ void CWinSystemX11::ResetOSScreensaver() { m_screensaverReset.StartZero(); XResetScreenSaver(m_dpy); - //need to flush the output buffer, since we don't check for events on m_dpy - XFlush(m_dpy); } } else @@ -437,36 +834,47 @@ void CWinSystemX11::ResetOSScreensaver() } } -void CWinSystemX11::NotifyAppActiveChange(bool bActivated) +void CWinSystemX11::NotifyAppFocusChange(bool bActivated) { if (bActivated && m_bWasFullScreenBeforeMinimize && !g_graphicsContext.IsFullScreenRoot()) g_graphicsContext.ToggleFullScreenRoot(); m_minimized = !bActivated; + if(bActivated) + m_bWasFullScreenBeforeMinimize = false; } + bool CWinSystemX11::Minimize() { m_bWasFullScreenBeforeMinimize = g_graphicsContext.IsFullScreenRoot(); if (m_bWasFullScreenBeforeMinimize) g_graphicsContext.ToggleFullScreenRoot(); - SDL_WM_IconifyWindow(); m_minimized = true; + XIconifyWindow(m_dpy, m_wmWindow, m_visual->screen); + return true; } + bool CWinSystemX11::Restore() { - return false; + return Show(true); } + bool CWinSystemX11::Hide() { XUnmapWindow(m_dpy, m_wmWindow); XSync(m_dpy, False); return true; } + bool CWinSystemX11::Show(bool raise) { - XMapWindow(m_dpy, m_wmWindow); + if(raise) + XMapRaised(m_dpy, m_wmWindow); + else + XMapWindow(m_dpy, m_wmWindow); + XSync(m_dpy, False); m_minimized = false; return true; @@ -474,41 +882,21 @@ bool CWinSystemX11::Show(bool raise) void CWinSystemX11::CheckDisplayEvents() { -#if defined(HAS_XRANDR) - bool bGotEvent(false); - bool bTimeout(false); - XEvent Event; - while (XCheckTypedEvent(m_dpy, m_RREventBase + RRScreenChangeNotify, &Event)) - { - if (Event.type == m_RREventBase + RRScreenChangeNotify) - { - CLog::Log(LOGDEBUG, "%s: Received RandR event %i", __FUNCTION__, Event.type); - bGotEvent = true; - } - XRRUpdateConfiguration(&Event); - } - - // check fail safe timer if (m_dpyLostTime && CurrentHostCounter() - m_dpyLostTime > (uint64_t)3 * CurrentHostFrequency()) { CLog::Log(LOGERROR, "%s - no display event after 3 seconds", __FUNCTION__); - bTimeout = true; + OnResetDevice(); } +} - if (bGotEvent || bTimeout) - { - CLog::Log(LOGDEBUG, "%s - notify display reset event", __FUNCTION__); - - CSingleLock lock(m_resourceSection); - - // tell any shared resources - for (vector::iterator i = m_resources.begin(); i != m_resources.end(); i++) - (*i)->OnResetDevice(); +void CWinSystemX11::NotifyXRREvent() +{ + CLog::Log(LOGDEBUG, "%s - notify display reset event", __FUNCTION__); - // reset fail safe timer - m_dpyLostTime = 0; - } +#if defined(HAS_XRANDR) + g_xrandr.Query(XScreenCount(m_dpy), true); #endif + OnResetDevice(); } void CWinSystemX11::OnLostDevice() @@ -527,6 +915,19 @@ void CWinSystemX11::OnLostDevice() m_dpyLostTime = CurrentHostCounter(); } + +void CWinSystemX11::OnResetDevice() +{ + CSingleLock lock(m_resourceSection); + + // tell any shared resources + for (vector::iterator i = m_resources.begin(); i != m_resources.end(); i++) + (*i)->OnResetDevice(); + + // reset fail safe timer + m_dpyLostTime = 0; +} + void CWinSystemX11::Register(IDispResource *resource) { CSingleLock lock(m_resourceSection); diff --git a/xbmc/windowing/X11/WinSystemX11.h b/xbmc/windowing/X11/WinSystemX11.h index e7af3cb5ef121..cbb6291d45868 100644 --- a/xbmc/windowing/X11/WinSystemX11.h +++ b/xbmc/windowing/X11/WinSystemX11.h @@ -46,12 +46,22 @@ class CWinSystemX11 : public CWinSystemBase virtual bool ResizeWindow(int newWidth, int newHeight, int newLeft, int newTop); virtual bool SetFullScreen(bool fullScreen, RESOLUTION_INFO& res, bool blankOtherDisplays); virtual void UpdateResolutions(); - virtual int GetNumScreens() { return 1; } + virtual int GetNumScreens(); + virtual int GetCurrentScreen(); virtual void ShowOSMouse(bool show); virtual void ResetOSScreensaver(); virtual bool EnableFrameLimiter(); - virtual void NotifyAppActiveChange(bool bActivated); + virtual void OnMove(int x, int y) + { + if(!m_bFullScreen) + { + m_nLeft = x; + m_nTop = y; + } + } + + virtual void NotifyAppFocusChange(bool bGaining); virtual bool Minimize(); virtual bool Restore() ; @@ -63,27 +73,52 @@ class CWinSystemX11 : public CWinSystemBase // Local to WinSystemX11 only Display* GetDisplay() { return m_dpy; } GLXWindow GetWindow() { return m_glWindow; } + XVisualInfo* GetVisual() { return m_visual; } + void NotifyXRREvent(); + + bool IsWindowManagerControlled() { return m_wm_controlled; } protected: - bool RefreshGlxContext(); + void ProbeWindowManager(); + void RefreshWindowState(); void CheckDisplayEvents(); void OnLostDevice(); + void OnResetDevice(); - SDL_Surface* m_SDLSurface; + XVisualInfo* m_visual; GLXContext m_glContext; GLXWindow m_glWindow; Window m_wmWindow; Display* m_dpy; + + Cursor m_invisibleCursor; + Pixmap m_icon; bool m_bWasFullScreenBeforeMinimize; bool m_minimized; - int m_RREventBase; CCriticalSection m_resourceSection; std::vector m_resources; uint64_t m_dpyLostTime; + CStdString m_outputName; + int m_outputIndex; + + bool m_wm; + CStdString m_wm_name; + bool m_wm_fullscreen; + bool m_wm_controlled; + + Atom m_NET_SUPPORTING_WM_CHECK; + Atom m_NET_WM_STATE; + Atom m_NET_WM_STATE_FULLSCREEN; + Atom m_NET_WM_STATE_MAXIMIZED_VERT; + Atom m_NET_WM_STATE_MAXIMIZED_HORZ; + Atom m_NET_SUPPORTED; + Atom m_NET_WM_NAME; + Atom m_WM_DELETE_WINDOW; private: bool IsSuitableVisual(XVisualInfo *vInfo); static int XErrorHandler(Display* dpy, XErrorEvent* error); + bool SetResolution(RESOLUTION_INFO& res, bool fullScreen); CStopWatch m_screensaverReset; }; diff --git a/xbmc/windowing/X11/WinSystemX11GL.cpp b/xbmc/windowing/X11/WinSystemX11GL.cpp index f858f88f46321..857a38efc4574 100644 --- a/xbmc/windowing/X11/WinSystemX11GL.cpp +++ b/xbmc/windowing/X11/WinSystemX11GL.cpp @@ -40,6 +40,18 @@ CWinSystemX11GL::~CWinSystemX11GL() { } +bool CWinSystemX11GL::DestroyWindowSystem() +{ + if (m_glContext) + { + glFinish(); + glXMakeCurrent(m_dpy, None, NULL); + glXDestroyContext(m_dpy, m_glContext); + m_glContext = None; + } + return CWinSystemX11::DestroyWindowSystem(); +} + bool CWinSystemX11GL::PresentRenderImpl(const CDirtyRegionList& dirty) { CheckDisplayEvents(); @@ -197,13 +209,61 @@ bool CWinSystemX11GL::IsExtSupported(const char* extension) return m_glxext.find(name) != std::string::npos; } +bool CWinSystemX11GL::DestroyWindow() +{ + if(m_glContext) + { + glFinish(); + glXMakeCurrent(m_dpy, None, NULL); + glXDestroyContext(m_dpy, m_glContext); + m_glContext = None; + } + return CWinSystemX11::DestroyWindow(); +} + bool CWinSystemX11GL::CreateNewWindow(const CStdString& name, bool fullScreen, RESOLUTION_INFO& res, PHANDLE_EVENT_FUNC userFunction) { + GLint att[] = + { + GLX_RGBA, + GLX_RED_SIZE, 8, + GLX_GREEN_SIZE, 8, + GLX_BLUE_SIZE, 8, + GLX_ALPHA_SIZE, 8, + GLX_DEPTH_SIZE, 24, + GLX_DOUBLEBUFFER, + None + }; + + DestroyWindow(); + + m_visual = glXChooseVisual(m_dpy, res.iInternal, att); + if(m_visual == NULL) + { + CLog::Log(LOGERROR, "CWinSystemX11GL::CreateNewWindow - failed to create visual"); + return false; + } + if(!CWinSystemX11::CreateNewWindow(name, fullScreen, res, userFunction)) return false; + m_glContext = glXCreateContext(m_dpy, m_visual, NULL, True); + if (m_glContext == NULL) + { + CLog::Log(LOGERROR, "CWinSystemX11GL::CreateNewWindow - failed to create context"); + return false; + } + + if(glXMakeCurrent(m_dpy, m_wmWindow, m_glContext) == False) + { + CLog::Log(LOGERROR, "CWinSystemX11GL::CreateNewWindow - failed to make context current"); + return false; + } + + m_glWindow = m_wmWindow; + m_glxext = " "; - m_glxext += (const char*)glXQueryExtensionsString(m_dpy, DefaultScreen(m_dpy)); + m_glxext += (const char*)glXQueryExtensionsString(m_dpy, m_visual->screen); m_glxext += " "; CLog::Log(LOGDEBUG, "GLX_EXTENSIONS:%s", m_glxext.c_str()); @@ -240,6 +300,8 @@ bool CWinSystemX11GL::CreateNewWindow(const CStdString& name, bool fullScreen, R m_glXSwapIntervalMESA = NULL; + OnResetDevice(); + return true; } diff --git a/xbmc/windowing/X11/WinSystemX11GL.h b/xbmc/windowing/X11/WinSystemX11GL.h index 5267d5d335e0e..6bcff91012320 100644 --- a/xbmc/windowing/X11/WinSystemX11GL.h +++ b/xbmc/windowing/X11/WinSystemX11GL.h @@ -31,6 +31,8 @@ class CWinSystemX11GL : public CWinSystemX11, public CRenderSystemGL public: CWinSystemX11GL(); virtual ~CWinSystemX11GL(); + virtual bool DestroyWindowSystem(); + virtual bool DestroyWindow(); virtual bool CreateNewWindow(const CStdString& name, bool fullScreen, RESOLUTION_INFO& res, PHANDLE_EVENT_FUNC userFunction); virtual bool ResizeWindow(int newWidth, int newHeight, int newLeft, int newTop); virtual bool SetFullScreen(bool fullScreen, RESOLUTION_INFO& res, bool blankOtherDisplays); diff --git a/xbmc/windowing/X11/XRandR.cpp b/xbmc/windowing/X11/XRandR.cpp index d8e9161fa3ca6..e70836ac55a70 100644 --- a/xbmc/windowing/X11/XRandR.cpp +++ b/xbmc/windowing/X11/XRandR.cpp @@ -36,14 +36,12 @@ using namespace std; -CXRandR::CXRandR(bool query) +CXRandR::CXRandR() { m_bInit = false; - if (query) - Query(); } -bool CXRandR::Query(bool force) +bool CXRandR::Query(int screens, bool force) { if (!force) if (m_bInit) @@ -55,11 +53,21 @@ bool CXRandR::Query(bool force) return false; m_outputs.clear(); - m_current.clear(); + // query all screens + for(int screennum=0; screennumValue(), "screen") != 0) - { - // TODO ERROR - return false; - } for (TiXmlElement* output = pRootElement->FirstChildElement("output"); output; output = output->NextSiblingElement("output")) { @@ -92,12 +95,20 @@ bool CXRandR::Query(bool force) xoutput.name.TrimLeft(" \n\r\t"); xoutput.name.TrimRight(" \n\r\t"); xoutput.isConnected = (strcasecmp(output->Attribute("connected"), "true") == 0); + xoutput.screen = screennum; xoutput.w = (output->Attribute("w") != NULL ? atoi(output->Attribute("w")) : 0); xoutput.h = (output->Attribute("h") != NULL ? atoi(output->Attribute("h")) : 0); xoutput.x = (output->Attribute("x") != NULL ? atoi(output->Attribute("x")) : 0); xoutput.y = (output->Attribute("y") != NULL ? atoi(output->Attribute("y")) : 0); xoutput.wmm = (output->Attribute("wmm") != NULL ? atoi(output->Attribute("wmm")) : 0); xoutput.hmm = (output->Attribute("hmm") != NULL ? atoi(output->Attribute("hmm")) : 0); + if (output->Attribute("rotation") != NULL + && (strcasecmp(output->Attribute("rotation"), "left") == 0 || strcasecmp(output->Attribute("rotation"), "right") == 0)) + { + xoutput.isRotated = true; + } + else + xoutput.isRotated = false; if (!xoutput.isConnected) continue; @@ -111,12 +122,14 @@ bool CXRandR::Query(bool force) xmode.hz = atof(mode->Attribute("hz")); xmode.w = atoi(mode->Attribute("w")); xmode.h = atoi(mode->Attribute("h")); + if(xoutput.isRotated) + std::swap(xmode.w, xmode.h); + xmode.isPreferred = (strcasecmp(mode->Attribute("preferred"), "true") == 0); xmode.isCurrent = (strcasecmp(mode->Attribute("current"), "true") == 0); xoutput.modes.push_back(xmode); if (xmode.isCurrent) { - m_current.push_back(xoutput); hascurrent = true; } } @@ -130,47 +143,21 @@ bool CXRandR::Query(bool force) std::vector CXRandR::GetModes(void) { - Query(); return m_outputs; } -void CXRandR::SaveState() -{ - Query(true); -} - -void CXRandR::RestoreState() -{ - vector::iterator outiter; - for (outiter=m_current.begin() ; outiter!=m_current.end() ; outiter++) - { - vector modes = (*outiter).modes; - vector::iterator modeiter; - for (modeiter=modes.begin() ; modeiter!=modes.end() ; modeiter++) - { - XMode mode = *modeiter; - if (mode.isCurrent) - { - SetMode(*outiter, mode); - return; - } - } - } -} - bool CXRandR::SetMode(XOutput output, XMode mode) { - if ((output.name == m_currentOutput && mode.id == m_currentMode) || (output.name == "" && mode.id == "")) + if (output.name == "" && mode.id == "") return true; - Query(); - // Make sure the output exists, if not -- complain and exit bool isOutputFound = false; XOutput outputFound; for (size_t i = 0; i < m_outputs.size(); i++) { - if (m_outputs[i].name == output.name) + if (m_outputs[i].name == output.name + && m_outputs[i].screen == output.screen) { isOutputFound = true; outputFound = m_outputs[i]; @@ -241,11 +228,9 @@ bool CXRandR::SetMode(XOutput output, XMode mode) return false; } - m_currentOutput = outputFound.name; - m_currentMode = modeFound.id; char cmd[255]; if (getenv("XBMC_BIN_HOME")) - snprintf(cmd, sizeof(cmd), "%s/xbmc-xrandr --output %s --mode %s", getenv("XBMC_BIN_HOME"), outputFound.name.c_str(), modeFound.id.c_str()); + snprintf(cmd, sizeof(cmd), "%s/xbmc-xrandr --screen %d --output %s --mode %s", getenv("XBMC_BIN_HOME"), outputFound.screen, outputFound.name.c_str(), modeFound.id.c_str()); else return false; CLog::Log(LOGINFO, "XRANDR: %s", cmd); @@ -259,25 +244,14 @@ bool CXRandR::SetMode(XOutput output, XMode mode) return true; } -XOutput CXRandR::GetCurrentOutput() +XMode CXRandR::GetCurrentMode(int screen, CStdString outputName) { - Query(); - for (unsigned int j = 0; j < m_outputs.size(); j++) - { - if(m_outputs[j].isConnected) - return m_outputs[j]; - } - XOutput empty; - return empty; -} -XMode CXRandR::GetCurrentMode(CStdString outputName) -{ - Query(); XMode result; for (unsigned int j = 0; j < m_outputs.size(); j++) { - if (m_outputs[j].name == outputName || outputName == "") + if ((m_outputs[j].name == outputName || outputName == "") + && (m_outputs[j].screen == screen)) { for (unsigned int i = 0; i < m_outputs[j].modes.size(); i++) { @@ -295,7 +269,6 @@ XMode CXRandR::GetCurrentMode(CStdString outputName) void CXRandR::LoadCustomModeLinesToAllOutputs(void) { - Query(); CXBMCTinyXML xmlDoc; if (!xmlDoc.LoadFile("special://xbmc/userdata/ModeLines.xml")) @@ -343,6 +316,21 @@ void CXRandR::LoadCustomModeLinesToAllOutputs(void) } } +XOutput CXRandR::GetOutput(int screen, CStdString outputName) +{ + XOutput result; + for (unsigned int i = 0; i < m_outputs.size(); ++i) + { + if ((m_outputs[i].name == outputName || outputName == "") + && (m_outputs[i].screen == screen)) + { + result = m_outputs[i]; + break; + } + } + return result; +} + CXRandR g_xrandr; #endif // HAS_XRANDR diff --git a/xbmc/windowing/X11/XRandR.h b/xbmc/windowing/X11/XRandR.h index 2a269d001d01f..bcbd3de7a9aa7 100644 --- a/xbmc/windowing/X11/XRandR.h +++ b/xbmc/windowing/X11/XRandR.h @@ -76,9 +76,11 @@ class XOutput name=""; isConnected=false; w=h=x=y=wmm=hmm=0; + screen=0; } CStdString name; bool isConnected; + int screen; int w; int h; int x; @@ -86,31 +88,24 @@ class XOutput int wmm; int hmm; std::vector modes; + bool isRotated; }; class CXRandR { public: - CXRandR(bool query=false); - bool Query(bool force=false); + CXRandR(); + bool Query(int screens, bool force); std::vector GetModes(void); - XOutput GetCurrentOutput(); - XMode GetCurrentMode(CStdString outputName); + XMode GetCurrentMode(int screen, CStdString outputName); + XOutput GetOutput(int screen, CStdString outputName); bool SetMode(XOutput output, XMode mode); void LoadCustomModeLinesToAllOutputs(void); - void SaveState(); - void RestoreState(); - //bool Has1080i(); - //bool Has1080p(); - //bool Has720p(); - //bool Has480p(); private: + bool Query(int screen); bool m_bInit; - std::vector m_current; std::vector m_outputs; - CStdString m_currentOutput; - CStdString m_currentMode; }; extern CXRandR g_xrandr; diff --git a/xbmc/windowing/windows/WinSystemWin32.cpp b/xbmc/windowing/windows/WinSystemWin32.cpp index 6a0e33520d614..24f2b964d7a6a 100644 --- a/xbmc/windowing/windows/WinSystemWin32.cpp +++ b/xbmc/windowing/windows/WinSystemWin32.cpp @@ -43,6 +43,7 @@ CWinSystemWin32::CWinSystemWin32() PtrGetGestureInfo = NULL; m_ValidWindowedPosition = false; m_IsAlteringWindow = false; + m_nScreen = 0; } CWinSystemWin32::~CWinSystemWin32() diff --git a/xbmc/windowing/windows/WinSystemWin32.h b/xbmc/windowing/windows/WinSystemWin32.h index de325cb823cb1..b39384309aa12 100644 --- a/xbmc/windowing/windows/WinSystemWin32.h +++ b/xbmc/windowing/windows/WinSystemWin32.h @@ -181,6 +181,7 @@ class CWinSystemWin32 : public CWinSystemBase int m_nPrimary; bool m_ValidWindowedPosition; bool m_IsAlteringWindow; + int m_nScreen; }; extern HWND g_hWnd;