Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Dropsdl #1175

Closed
wants to merge 25 commits into from

6 participants

@elupus
Collaborator

This pull request rids us of SDL for events and window system.

I'm a bit hesitant to the fix for separate screen's. It get a bit messy with extending resolution structure for this. Wasn't sure what the call the value also it's still broken since it need a skin refresh since we apparently have not implemented OnDeviceReset() callbacks on GLX systems.

This tries to replace #848, but it isn't fully complete. So @FernetMenta may have to re-add anything that i've missed.

It clearly need testing.

@theuni
Owner

Nice! Drop it from the tallest building... :)

(Only glanced through it, not qualified to comment on the X stuff)

@mkortstiege
Collaborator

There's a problem with the title and icon here. Within the task-switcher (alt-tab) the title is "unkown" and no icon is shown. Maybe its a gnome-shell issue, not sure yet.

@elupus
Collaborator
@mkortstiege
Collaborator

That's odd :)

@FernetMenta
Collaborator

There's a couple of things obviously got broken. e.g.

  • toggle full screen, can't size window in horizontal direction
  • XBMC icon broken: caused by a change in CTexture
  • minimize window: alt-tab: XBMC stays in background instead of getting minimized

A couple of others which I had already fixed:

  • listen to external xrandr events and take action: consider a user who starts XBMC, then turns on an av receiver or projector. This version does not update resolutions and can't switch to the new output
  • gui corruption on AMD is back. The correct sequence is to first change resolution, then refresh window on xrandr event. There was a reason why I re-created the window on this event.
  • It must be distinguished between systems running a window manager and systems which do not. If no WM is present the redirect_override flag has to be set instead of dealing with NET_WM*. Not doing this can decrease performance on NVdias ystems running vdpau.
  • I don't see the patch for the screensaver, If the scrren has switched off due to inactivity XBMC won't be able to activate the screen again.

I'm a bit hesitant to the fix for separate screen's.

This is the use case which is even more likely than having a single screen. On NVidia systems you get tearing when having two monitors connected to a single screen.

It get a bit messy with extending resolution structure for this

Why? No need to load resolutions of a screen where XBMC is not displayed.

@elupus
Collaborator
@FernetMenta
Collaborator

Don't have time to answer the other points, but this I know for sure as it was tested and proved. Weird but this is like it works in reality. (There are a couple of hundred users tested this already and I collected feedback)

A tester:

Finally:
"swa.override_redirect = False;" gives many drops, how describes above.

"swa.override_redirect = fullscreen ? True : False;" gives smooth picture in fullscreen.

I tested twice.

@elupus
Collaborator
@FernetMenta
Collaborator

Right. He is using a windowmanager, which is redirecting even fullscreen windows. (many do). So we might have to go that route for all situation. I was just saying that it would not make sense to only do it for the non windowmanager case. It's the with windowmanager case that is problematic.

No, he was no WM (OpenElec). I have solved it by detecting the presents of a WM with this function:
FernetMenta@02289bb

BTW: setting the overrive_redirect flag when a WM is present (like KDE), then it kills performance in that case.

@FernetMenta
Collaborator

Testing on Ubuntu 12.04 with Unity and Fluxbox:

1)
toggle full-screen:
Unity:
When switching to windowed mode XBMC is horizontally stretched, docked at the menu bar and to the right border of the screen. I can resize and move the window only in vertical direction.
Fluxbox:
The origin of XBMC is outside the visible area of the screen, somewhere behind the upper left. I can resize the window in both direction but not move because the title bar is outside the screen.

2)
alt-tab
This is not working when XBMC is full-screen. When hitting alt-tab XBMC becomes the wall paper in the background. Same behavior on Unity and Fluxbox.

Because that is not how all other arches work. I don't mind changing it, but it must be done for all arches. I don't want a bunch of special cases for X11.

Different architectures have different functions and terminology. Why confusing the user with naming an output a screen just for the sake it is called a screen on a different platform.

xbmc/windowing/WinEventsX11.cpp
((447 lines not shown))
+ {
+ 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 = xevent.xkey.state;
+ newEvent.key.type = xevent.xkey.type;
+ ret |= ProcessKey(newEvent, 500);
+ }
+ if (keys.length() > 0)
+ {
+ newEvent.key.keysym.scancode = xevent.xkey.keycode;
+ xkeysym = XLookupKeysym(&xevent.xkey, 0);
@elupus Collaborator
elupus added a note

@FernetMenta Could you explain this? Shouldn't Xutf8LookupString should have filled this in earilier?

@FernetMenta Collaborator

That's what I have expected too in the first iteration but it broke behavior to what we had with SDL. SDL calls a depreciated function at this place: XKeycodeToKeysym. So I replaced this with XLookupKeysym.
The problem was key combinations like ctrl-s or ctrt-S. Without XLookupKeysym the character was always recognized as lower case (or upper case, don't remember exactly. So user defined keycodes in keyboard.xml were broken.

@elupus Collaborator
elupus added a note

So we never want to use what Xutf8... has returned then? Ie i can move that call to right after with a comment?

@FernetMenta Collaborator

We want to use the string we get from Xutf8... but not the keysym

@elupus Collaborator
elupus added a note
@FernetMenta Collaborator

Sorry, didn't get this right. Sounds like a good idea.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@elupus
Collaborator

some further along. i've hooked up running xbmc with override_redirect when run as standalone. it's really messy to use since you have to handle input focus yourself.

It should be possible to only use override_redirect in fullscreen only, like was done before. That might be for the best since so many window manager are slow otherwise.

@FernetMenta
Collaborator

Not sure if the issues mentioned above are supposed to be fixed. alt-tab and toggle fullscreen do still not work.

@elupus
Collaborator

I assume you where not running standalone? (that is broken in this anyway). What window manager where you using? cause i don't even get that keypress when running under unity. it swallows it and handles it itself.

@FernetMenta
Collaborator

I tested on Ubuntu 12.04 with Unity 2D and Fluxbox. Still the same behavior as described above.

@mkortstiege
Collaborator

What's the status here? Are we going to drop SDL before frodo?

@elupus
Collaborator
FernetMenta and others added some commits
@FernetMenta FernetMenta X11: add SDL joystick until we have a better solution 9aef2c8
@FernetMenta FernetMenta X11: Add xbmc icon
This does not take icon transparancy into account, so
could look weird.
0a51b45
@elupus elupus X11: factor out code that reset device after lost 5b6caeb
@elupus elupus X11: move handling of xrandr events 18311d8
@elupus elupus X11: support multiple xserver screens c7c6cb2
@elupus elupus X11: support multiple xrandr displays 0b80d65
@elupus elupus X11: replace SDL window and event handling with native xlib 25decc3
@elupus elupus X11: simplify set and reset of locale 886280b
@elupus elupus X11: XWMHints must be allocated by XAllocVMHints since it can be exte…
…nded
d33967f
@FernetMenta FernetMenta add missing keys to xbmc keytable a9f2e95
@elupus elupus X11: support rotated screens. fd0e37a
@elupus elupus [win32] move m_nScreen windowing variable to only system using it. 4988dbd
@elupus elupus X11: re-evaluate vsync method after having modified window
It could have moved between outputs on different hardware
bfef61d
@elupus elupus x11: refactor WinEventsX11 to be a singleton class
Also
X11: fix mixup of what XBMC_KeyboardEvent state/type should contain
X11: allow fallback to XLookupString to also support long strings
b87b5ff
@elupus elupus X11 (squash): don't resize window after (full)screen switch if we hav…
…e wm

Resizing can trigger VM to set window as maximized or other.
5dee1c7
@elupus elupus X11: use override_redirect if window manager doesn't support full scr…
…een or standalone
daa9d27
@elupus elupus X11: make sure we set resolution before we create window
Unity messes up on resolution switches and fails to re-fullscreen
937edd9
@elupus elupus debug stuff 99e7201
@elupus elupus squash into drop sdl 9aa3f92
@elupus
Collaborator

So rebased, not all squashed together yet. I'm not fully happy with it yet, but @FernetMenta would be nice with some testing and feedback on what works and what doesn't.

It should now detect if we have a window manager that support fullscreen. If not it uses override_redirect. It will also switch to that if you run it with --standalone switch.

@elupus
Collaborator

Some known things I've not resolved yet.
1. Alt+tab when override redirect is used (we must handle Window switches ourselves)
2. Switching between x11 screen lead to loss of textures. I wonder if one can avoid the texture loss by moving the Window without recreating it. It aught to work for when both are in same gfx at least.

@theuni
Owner

@elupus worth mentioning that i'm in the middle of a complete rewrite for egl, which will be followed by a rewrite of rendering. The goal is to get a clean abstraction where everything is dynamic as possible, meaning that you could run the same binary on X, wayland, fbdev, etc, and switch between gl/gles/directfb/etc all on the fly.

I'm getting reasonably far along, I'll push it up somewhere once it runs on a few platforms.

I don't think it's directly relevant here (except for the egl+X case), but I figured I'd throw it out there.

@theuni
Owner

Whew, I wrote my comment, then like a genius hit the green 'merge pull request' button. Thanks for making me verify, Github!

@FernetMenta
Collaborator

Toggle full screen and minimize do not work on my system. Will investigate ...

@elupus
Collaborator
@FernetMenta
Collaborator

I had to fix a compile error in WinEventsX11: CApplicationMessenger.

When started in windowed mode togglefullscreen works, it does not the other way round. It becomes windowed but with size of full screen. No chance to resize then.

@elupus
Collaborator

Hehe, i thought you actually did merge it :)

@elupus
Collaborator

Right.. that would mean that our resolution setup for windowed is getting b0rk some where. it should be respecting that.

xbmc/windowing/WinEventsX11.cpp
((734 lines not shown))
+ return true;
+
+ default:
+ return false;
+ }
+ }
+ return false;
+}
+
+bool CWinEventsX11::ProcessKeyRepeat()
+{
+ if (WinEvents && (m_lastKey.type == XBMC_KEYDOWN))
+ {
+ if (m_repeatKeyTimeout.IsTimePast())
+ {
+ return ProcessKey(m_lastKey, 10);
@FernetMenta Collaborator

Should we set this to 100ms and/or make it configurable by advancedsettings? I see no reason why this is 10ms. MCE remotes registered as keyboards don't work that well.

@elupus Collaborator
elupus added a note

I have found some code that actually uses the X11 key-repeats instead of generating them ourselves. I think that is much better.

@elupus Collaborator
elupus added a note

Check the ProcessKeyRelease. That will ignore keyup's that are actually key repeats.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@Memphiz
Owner

The problem is not the green button - its the blue one afterwards ;)

@FernetMenta
Collaborator

Right.. that would mean that our resolution setup for windowed is getting b0rk some where. it should be respecting that.

resolution is ok, it just gets not set. SetFullScreen just resets NET_WM_STATE_FULLSCREEN without doing a ResizeWindow.

elupus added some commits
@elupus elupus X11: real XConfigureEvents have position relative parent 0ac9a81
@elupus elupus squash fix compile error 2e2db91
@elupus elupus squash somewhere
Don't re-write left/top in fullscreen since that is only way of
knowing normal window size.
8b33140
@elupus elupus X11: create _NET_WM_STATE_FULLSCREEN windows with windowed size
Window manager uses created size to store original size before going
fullscreen. This makes it possible to restore size after fullscreen
again.
f51b37d
@elupus
Collaborator

The problem was that the WM uses the XCreateWindow size when it restores from fullscreen. So we must create the window with a windowed size, and let the WM do it fullscreening job

@elupus
Collaborator

Could you try with mce remotes now and see if it works allright?

@FernetMenta
Collaborator

ToggleFullScreen still does not work on my system. Ubuntu 12.04 with Ubuntu 2D. Window width/height is always 1855x1056 when switching from fullscreen to windowed (starting xbmc fullscreen).
The window attributes seem to be unreliable. Why not explicitly resize the window when entering windowed mode?

@FernetMenta
Collaborator

MCE remote seems to work fine.

@elupus
Collaborator
@FernetMenta
Collaborator

I remember I had trouble with that too. Once fullscreen I found no reliable way to get back in windowed mode and ended up in recreating the window. SDL used 3 windows, mapped/unmapped the fullscreen and wm window, and reparented the gl window. I preferred to have a single window.

@MartijnKaijser

see #3505

@tru tru referenced this pull request from a commit in plexinc/plex-home-theater-public
@tru tru Fix black screen when music screensaver is activated.
This was due to the fact that XBMC code always called
ActivateWindow(WINDOW_VIS) when we wanted to call ActivateVisualizer()
which handles NOW_PLAYING and vis.

Fixes #1175
d028dab
@tru tru referenced this pull request from a commit in RasPlex/plex-home-theatre
@tru tru Fix black screen when music screensaver is activated.
This was due to the fact that XBMC code always called
ActivateWindow(WINDOW_VIS) when we wanted to call ActivateVisualizer()
which handles NOW_PLAYING and vis.

Fixes #1175
542ce83
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Sep 21, 2012
  1. @FernetMenta @elupus

    X11: add SDL joystick until we have a better solution

    FernetMenta authored elupus committed
  2. @FernetMenta @elupus

    X11: Add xbmc icon

    FernetMenta authored elupus committed
    This does not take icon transparancy into account, so
    could look weird.
  3. @elupus
  4. @elupus
  5. @elupus
  6. @elupus
  7. @elupus
  8. @elupus
  9. @elupus
  10. @FernetMenta @elupus

    add missing keys to xbmc keytable

    FernetMenta authored elupus committed
  11. @elupus

    X11: support rotated screens.

    elupus authored
  12. @elupus
  13. @elupus

    X11: re-evaluate vsync method after having modified window

    elupus authored
    It could have moved between outputs on different hardware
  14. @elupus

    x11: refactor WinEventsX11 to be a singleton class

    elupus authored
    Also
    X11: fix mixup of what XBMC_KeyboardEvent state/type should contain
    X11: allow fallback to XLookupString to also support long strings
  15. @elupus

    X11 (squash): don't resize window after (full)screen switch if we hav…

    elupus authored
    …e wm
    
    Resizing can trigger VM to set window as maximized or other.
  16. @elupus
  17. @elupus

    X11: make sure we set resolution before we create window

    elupus authored
    Unity messes up on resolution switches and fails to re-fullscreen
  18. @elupus

    debug stuff

    elupus authored
  19. @elupus

    squash into drop sdl

    elupus authored
Commits on Sep 23, 2012
  1. @elupus
  2. @elupus

    squash fix compile error

    elupus authored
  3. @elupus

    squash somewhere

    elupus authored
    Don't re-write left/top in fullscreen since that is only way of
    knowing normal window size.
  4. @elupus

    X11: create _NET_WM_STATE_FULLSCREEN windows with windowed size

    elupus authored
    Window manager uses created size to store original size before going
    fullscreen. This makes it possible to restore size after fullscreen
    again.
  5. @elupus
  6. @elupus
This page is out of date. Refresh to see the latest.
View
2  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
View
10 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
View
3  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;
}
};
View
2  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"}
View
1  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 );
View
5 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
View
1  xbmc/windowing/Makefile
@@ -1,6 +1,7 @@
SRCS=WinEventsSDL.cpp \
WinEventsLinux.cpp \
WinSystem.cpp \
+ WinEventsX11.cpp \
LIB=windowing.a
View
4 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
View
750 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 <X11/Xlib.h>
+#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 <X11/extensions/Xrandr.h>
+#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<uint32_t, uint32_t>::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
View
67 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 <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include "threads/SystemClock.h"
+#include <map>
+
+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<uint32_t,uint32_t> m_symLookupTable;
+ int m_keymodState;
+ int m_RREventBase;
+};
View
1  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;
}
View
4 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;
};
View
869 xbmc/windowing/X11/WinSystemX11.cpp
@@ -22,36 +22,66 @@
#ifdef HAS_GLX
-#include <SDL/SDL_syswm.h>
#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 <vector>
#include "threads/SingleLock.h"
#include <X11/Xlib.h>
+#include <X11/Xatom.h>
#include "cores/VideoRenderers/RenderManager.h"
#include "utils/TimeUtils.h"
+#include "Application.h"
#if defined(HAS_XRANDR)
#include <X11/extensions/Xrandr.h>
#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; i<image.height;++i)
+ {
+ for (j=0; j<image.width;++j)
{
- glXMakeCurrent(m_dpy, None, NULL);
- glXDestroyContext(m_dpy, m_glContext);
+ unsigned int pos = (image.height-i-1)*pitch+j*3;
+ unsigned int r, g, b;
+ r = (buf[pos+2] * rRatio);
+ g = (buf[pos+1] * gRatio);
+ b = (buf[pos+0] * bRatio);
+ r &= vis->red_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<XOutput> 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<XOutput>::iterator outiter;
- vector<XOutput> 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<XOutput> 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())