/*
Simple DirectMedia Layer
- Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
+ Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
#include "../SDL_pixels_c.h"
#include "../../events/SDL_keyboard_c.h"
#include "../../events/SDL_mouse_c.h"
-#include "../SDL_vulkan_c.h"
#include "SDL_x11video.h"
#include "SDL_x11mouse.h"
#include "SDL_timer.h"
#include "SDL_syswm.h"
-#include "SDL_assert.h"
+#include "SDL_log.h"
#define _NET_WM_STATE_REMOVE 0l
#define _NET_WM_STATE_ADD 1l
-#define _NET_WM_STATE_TOGGLE 2l
static Bool isMapNotify(Display *dpy, XEvent *ev, XPointer win)
{
{
SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata;
Display *display = videodata->display;
+ /* !!! FIXME: just dereference videodata below instead of copying to locals. */
Atom _NET_WM_STATE = videodata->_NET_WM_STATE;
/* Atom _NET_WM_STATE_HIDDEN = videodata->_NET_WM_STATE_HIDDEN; */
Atom _NET_WM_STATE_FOCUSED = videodata->_NET_WM_STATE_FOCUSED;
Atom _NET_WM_STATE_MAXIMIZED_VERT = videodata->_NET_WM_STATE_MAXIMIZED_VERT;
Atom _NET_WM_STATE_MAXIMIZED_HORZ = videodata->_NET_WM_STATE_MAXIMIZED_HORZ;
Atom _NET_WM_STATE_FULLSCREEN = videodata->_NET_WM_STATE_FULLSCREEN;
- Atom atoms[5];
+ Atom _NET_WM_STATE_ABOVE = videodata->_NET_WM_STATE_ABOVE;
+ Atom _NET_WM_STATE_SKIP_TASKBAR = videodata->_NET_WM_STATE_SKIP_TASKBAR;
+ Atom _NET_WM_STATE_SKIP_PAGER = videodata->_NET_WM_STATE_SKIP_PAGER;
+ Atom atoms[16];
int count = 0;
/* The window manager sets this property, we shouldn't set it.
atoms[count++] = _NET_WM_STATE_HIDDEN;
}
*/
+
+ if (flags & SDL_WINDOW_ALWAYS_ON_TOP) {
+ atoms[count++] = _NET_WM_STATE_ABOVE;
+ }
+ if (flags & SDL_WINDOW_SKIP_TASKBAR) {
+ atoms[count++] = _NET_WM_STATE_SKIP_TASKBAR;
+ atoms[count++] = _NET_WM_STATE_SKIP_PAGER;
+ }
if (flags & SDL_WINDOW_INPUT_FOCUS) {
atoms[count++] = _NET_WM_STATE_FOCUSED;
}
if (flags & SDL_WINDOW_FULLSCREEN) {
atoms[count++] = _NET_WM_STATE_FULLSCREEN;
}
+
+ SDL_assert(count <= SDL_arraysize(atoms));
+
if (count > 0) {
X11_XChangeProperty(display, xwindow, _NET_WM_STATE, XA_ATOM, 32,
PropModeReplace, (unsigned char *)atoms, count);
}
if (maximized == 3) {
flags |= SDL_WINDOW_MAXIMIZED;
- } else if (fullscreen == 1) {
+ }
+
+ if (fullscreen == 1) {
flags |= SDL_WINDOW_FULLSCREEN;
}
+
+ /* If the window is unmapped, numItems will be zero and _NET_WM_STATE_HIDDEN
+ * will not be set. Do an additional check to see if the window is unmapped
+ * and mark it as SDL_WINDOW_HIDDEN if it is.
+ */
+ {
+ XWindowAttributes attr;
+ SDL_memset(&attr,0,sizeof(attr));
+ X11_XGetWindowAttributes(videodata->display, xwindow, &attr);
+ if (attr.map_state == IsUnmapped) {
+ flags |= SDL_WINDOW_HIDDEN;
+ }
+ }
X11_XFree(propertyValue);
}
XSizeHints *sizehints;
XWMHints *wmhints;
XClassHint *classhints;
- const long _NET_WM_BYPASS_COMPOSITOR_HINT_ON = 1;
Atom _NET_WM_BYPASS_COMPOSITOR;
Atom _NET_WM_WINDOW_TYPE;
- Atom _NET_WM_WINDOW_TYPE_NORMAL;
+ Atom wintype;
+ const char *wintype_name = NULL;
+ long compositor = 1;
Atom _NET_WM_PID;
Atom XdndAware, xdnd_version = 5;
long fevent = 0;
#if SDL_VIDEO_OPENGL_EGL
if (_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_ES
#if SDL_VIDEO_OPENGL_GLX
- && ( !_this->gl_data || ! _this->gl_data->HAS_GLX_EXT_create_context_es2_profile )
+ && ( !_this->gl_data || X11_GL_UseEGL(_this) )
#endif
) {
vinfo = X11_GLES_GetVisual(_this, display, screen);
depth = displaydata->depth;
}
-#if SDL_VIDEO_VULKAN
- if (window->flags & SDL_WINDOW_VULKAN) {
- if (!_this->vulkan_GetInstanceExtensions) {
- SDL_SetError("No Vulkan support in video driver");
- }
- if (_this->vulkan_LoadLibrary(_this, NULL) < 0) {
- SDL_SetError("Fail to load Vulkan Library");
- }
- }
-#endif
- xattr.override_redirect = False;
+ xattr.override_redirect = ((window->flags & SDL_WINDOW_TOOLTIP) || (window->flags & SDL_WINDOW_POPUP_MENU)) ? True : False;
xattr.background_pixmap = None;
xattr.border_pixel = 0;
/* Setup the input hints so we get keyboard input */
wmhints = X11_XAllocWMHints();
wmhints->input = True;
- wmhints->flags = InputHint;
+ wmhints->window_group = data->window_group;
+ wmhints->flags = InputHint | WindowGroupHint;
/* Setup the class hints so we can get an icon (AfterStep) */
classhints = X11_XAllocClassHint();
X11_XFree(classhints);
/* Set the PID related to the window for the given hostname, if possible */
if (data->pid > 0) {
+ long pid = (long) data->pid;
_NET_WM_PID = X11_XInternAtom(display, "_NET_WM_PID", False);
X11_XChangeProperty(display, w, _NET_WM_PID, XA_CARDINAL, 32, PropModeReplace,
- (unsigned char *)&data->pid, 1);
+ (unsigned char *) &pid, 1);
}
/* Set the window manager state */
X11_SetNetWMState(_this, w, window->flags);
- /* Let the window manager know we're a "normal" window */
+ compositor = 2; /* don't disable compositing except for "normal" windows */
+
+ if (window->flags & SDL_WINDOW_UTILITY) {
+ wintype_name = "_NET_WM_WINDOW_TYPE_UTILITY";
+ } else if (window->flags & SDL_WINDOW_TOOLTIP) {
+ wintype_name = "_NET_WM_WINDOW_TYPE_TOOLTIP";
+ } else if (window->flags & SDL_WINDOW_POPUP_MENU) {
+ wintype_name = "_NET_WM_WINDOW_TYPE_POPUP_MENU";
+ } else {
+ wintype_name = "_NET_WM_WINDOW_TYPE_NORMAL";
+ compositor = 1; /* disable compositing for "normal" windows */
+ }
+
+ /* Let the window manager know what type of window we are. */
_NET_WM_WINDOW_TYPE = X11_XInternAtom(display, "_NET_WM_WINDOW_TYPE", False);
- _NET_WM_WINDOW_TYPE_NORMAL = X11_XInternAtom(display, "_NET_WM_WINDOW_TYPE_NORMAL", False);
+ wintype = X11_XInternAtom(display, wintype_name, False);
X11_XChangeProperty(display, w, _NET_WM_WINDOW_TYPE, XA_ATOM, 32,
- PropModeReplace,
- (unsigned char *)&_NET_WM_WINDOW_TYPE_NORMAL, 1);
-
- _NET_WM_BYPASS_COMPOSITOR = X11_XInternAtom(display, "_NET_WM_BYPASS_COMPOSITOR", False);
- X11_XChangeProperty(display, w, _NET_WM_BYPASS_COMPOSITOR, XA_CARDINAL, 32,
- PropModeReplace,
- (unsigned char *)&_NET_WM_BYPASS_COMPOSITOR_HINT_ON, 1);
+ PropModeReplace, (unsigned char *)&wintype, 1);
+ if (SDL_GetHintBoolean(SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, SDL_TRUE)) {
+ _NET_WM_BYPASS_COMPOSITOR = X11_XInternAtom(display, "_NET_WM_BYPASS_COMPOSITOR", False);
+ X11_XChangeProperty(display, w, _NET_WM_BYPASS_COMPOSITOR, XA_CARDINAL, 32,
+ PropModeReplace,
+ (unsigned char *)&compositor, 1);
+ }
{
- Atom protocols[2];
+ Atom protocols[3];
int proto_count = 0;
- const char *ping_hint;
- protocols[proto_count] = data->WM_DELETE_WINDOW; /* Allow window to be deleted by the WM */
- proto_count++;
-
- ping_hint = SDL_GetHint(SDL_HINT_VIDEO_X11_NET_WM_PING);
+ protocols[proto_count++] = data->WM_DELETE_WINDOW; /* Allow window to be deleted by the WM */
+ protocols[proto_count++] = data->WM_TAKE_FOCUS; /* Since we will want to set input focus explicitly */
+
/* Default to using ping if there is no hint */
- if (!ping_hint || SDL_atoi(ping_hint)) {
- protocols[proto_count] = data->_NET_WM_PING; /* Respond so WM knows we're alive */
- proto_count++;
+ if (SDL_GetHintBoolean(SDL_HINT_VIDEO_X11_NET_WM_PING, SDL_TRUE)) {
+ protocols[proto_count++] = data->_NET_WM_PING; /* Respond so WM knows we're alive */
}
SDL_assert(proto_count <= sizeof(protocols) / sizeof(protocols[0]));
if ((window->flags & SDL_WINDOW_OPENGL) &&
_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_ES
#if SDL_VIDEO_OPENGL_GLX
- && ( !_this->gl_data || ! _this->gl_data->HAS_GLX_EXT_create_context_es2_profile )
+ && ( !_this->gl_data || X11_GL_UseEGL(_this) )
#endif
) {
#if SDL_VIDEO_OPENGL_EGL
return SDL_SetError("Could not create GLES window surface");
}
#else
- return SDL_SetError("Could not create GLES window surface (no EGL support available)");
+ return SDL_SetError("Could not create GLES window surface (EGL support not configured)");
#endif /* SDL_VIDEO_OPENGL_EGL */
}
#endif
SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
Display *display = data->videodata->display;
- X11_XMoveWindow(display, data->xwindow, window->x, window->y);
+ X11_XMoveWindow(display, data->xwindow, window->x - data->border_left, window->y - data->border_top);
X11_XFlush(display);
}
/* See comment in X11_SetWindowSize. */
X11_XResizeWindow(display, data->xwindow, window->w, window->h);
- X11_XMoveWindow(display, data->xwindow, window->x, window->y);
+ X11_XMoveWindow(display, data->xwindow, window->x - data->border_left, window->y - data->border_top);
X11_XRaiseWindow(display, data->xwindow);
}
/* See comment in X11_SetWindowSize. */
X11_XResizeWindow(display, data->xwindow, window->w, window->h);
- X11_XMoveWindow(display, data->xwindow, window->x, window->y);
+ X11_XMoveWindow(display, data->xwindow, window->x - data->border_left, window->y - data->border_top);
X11_XRaiseWindow(display, data->xwindow);
}
and transitioning from windowed to fullscreen in Unity.
*/
X11_XResizeWindow(display, data->xwindow, window->w, window->h);
- X11_XMoveWindow(display, data->xwindow, window->x, window->y);
+ X11_XMoveWindow(display, data->xwindow, window->x - data->border_left, window->y - data->border_top);
X11_XRaiseWindow(display, data->xwindow);
} else {
X11_XResizeWindow(display, data->xwindow, window->w, window->h);
X11_XFlush(display);
}
+int
+X11_GetWindowBordersSize(_THIS, SDL_Window * window, int *top, int *left, int *bottom, int *right)
+{
+ SDL_WindowData *data = (SDL_WindowData *)window->driverdata;
+
+ *left = data->border_left;
+ *right = data->border_right;
+ *top = data->border_top;
+ *bottom = data->border_bottom;
+
+ return 0;
+}
+
+int
+X11_SetWindowOpacity(_THIS, SDL_Window * window, float opacity)
+{
+ SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
+ Display *display = data->videodata->display;
+ Atom _NET_WM_WINDOW_OPACITY = data->videodata->_NET_WM_WINDOW_OPACITY;
+
+ if (opacity == 1.0f) {
+ X11_XDeleteProperty(display, data->xwindow, _NET_WM_WINDOW_OPACITY);
+ } else {
+ const Uint32 FullyOpaque = 0xFFFFFFFF;
+ const long alpha = (long) ((double)opacity * (double)FullyOpaque);
+ X11_XChangeProperty(display, data->xwindow, _NET_WM_WINDOW_OPACITY, XA_CARDINAL, 32,
+ PropModeReplace, (unsigned char *)&alpha, 1);
+ }
+
+ return 0;
+}
+
+int
+X11_SetWindowModalFor(_THIS, SDL_Window * modal_window, SDL_Window * parent_window) {
+ SDL_WindowData *data = (SDL_WindowData *) modal_window->driverdata;
+ SDL_WindowData *parent_data = (SDL_WindowData *) parent_window->driverdata;
+ Display *display = data->videodata->display;
+
+ X11_XSetTransientForHint(display, data->xwindow, parent_data->xwindow);
+ return 0;
+}
+
+int
+X11_SetWindowInputFocus(_THIS, SDL_Window * window)
+{
+ if (X11_IsWindowMapped(_this, window)) {
+ SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
+ Display *display = data->videodata->display;
+ X11_XSetInputFocus(display, data->xwindow, RevertToNone, CurrentTime);
+ X11_XFlush(display);
+ return 0;
+ }
+ return -1;
+}
+
void
X11_SetWindowBordered(_THIS, SDL_Window * window, SDL_bool bordered)
{
}
void
+X11_SetWindowResizable(_THIS, SDL_Window * window, SDL_bool resizable)
+{
+ SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
+ Display *display = data->videodata->display;
+
+ XSizeHints *sizehints = X11_XAllocSizeHints();
+ long userhints;
+
+ X11_XGetWMNormalHints(display, data->xwindow, sizehints, &userhints);
+
+ if (resizable) {
+ /* FIXME: Is there a better way to get max window size from X? -flibit */
+ const int maxsize = 0x7FFFFFFF;
+ sizehints->min_width = window->min_w;
+ sizehints->min_height = window->min_h;
+ sizehints->max_width = (window->max_w == 0) ? maxsize : window->max_w;
+ sizehints->max_height = (window->max_h == 0) ? maxsize : window->max_h;
+ } else {
+ sizehints->min_width = window->w;
+ sizehints->min_height = window->h;
+ sizehints->max_width = window->w;
+ sizehints->max_height = window->h;
+ }
+ sizehints->flags |= PMinSize | PMaxSize;
+
+ X11_XSetWMNormalHints(display, data->xwindow, sizehints);
+
+ X11_XFree(sizehints);
+
+ /* See comment in X11_SetWindowSize. */
+ X11_XResizeWindow(display, data->xwindow, window->w, window->h);
+ X11_XMoveWindow(display, data->xwindow, window->x - data->border_left, window->y - data->border_top);
+ X11_XRaiseWindow(display, data->xwindow);
+
+ X11_XFlush(display);
+}
+
+void
X11_ShowWindow(_THIS, SDL_Window * window)
{
SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
/* Blocking wait for "MapNotify" event.
* We use X11_XIfEvent because pXWindowEvent takes a mask rather than a type,
* and XCheckTypedWindowEvent doesn't block */
- X11_XIfEvent(display, &event, &isMapNotify, (XPointer)&data->xwindow);
+ if(!(window->flags & SDL_WINDOW_FOREIGN))
+ X11_XIfEvent(display, &event, &isMapNotify, (XPointer)&data->xwindow);
X11_XFlush(display);
}
if (X11_IsWindowMapped(_this, window)) {
X11_XWithdrawWindow(display, data->xwindow, displaydata->screen);
/* Blocking wait for "UnmapNotify" event */
- X11_XIfEvent(display, &event, &isUnmapNotify, (XPointer)&data->xwindow);
+ if(!(window->flags & SDL_WINDOW_FOREIGN))
+ X11_XIfEvent(display, &event, &isUnmapNotify, (XPointer)&data->xwindow);
X11_XFlush(display);
}
}
if (X11_IsWindowMapped(_this, window)) {
XEvent e;
+ /*printf("SDL Window %p: sending _NET_ACTIVE_WINDOW with timestamp %lu\n", window, data->user_time);*/
+
SDL_zero(e);
e.xany.type = ClientMessage;
e.xclient.message_type = _NET_ACTIVE_WINDOW;
e.xclient.format = 32;
e.xclient.window = data->xwindow;
e.xclient.data.l[0] = 1; /* source indication. 1 = application */
- e.xclient.data.l[1] = CurrentTime;
+ e.xclient.data.l[1] = data->user_time;
e.xclient.data.l[2] = 0;
X11_XSendEvent(display, RootWindow(display, displaydata->screen), 0,
SetWindowActive(_this, window);
}
-/* This asks the Window Manager to handle fullscreen for us. Most don't do it right, though. */
+/* This asks the Window Manager to handle fullscreen for us. This is the modern way. */
static void
X11_SetWindowFullscreenViaWM(_THIS, SDL_Window * window, SDL_VideoDisplay * _display, SDL_bool fullscreen)
{
X11_XSendEvent(display, RootWindow(display, displaydata->screen), 0,
SubstructureNotifyMask | SubstructureRedirectMask, &e);
+
+ /* Fullscreen windows sometimes end up being marked maximized by
+ window managers. Force it back to how we expect it to be. */
+ if (!fullscreen && ((window->flags & SDL_WINDOW_MAXIMIZED) == 0)) {
+ SDL_zero(e);
+ e.xany.type = ClientMessage;
+ e.xclient.message_type = _NET_WM_STATE;
+ e.xclient.format = 32;
+ e.xclient.window = data->xwindow;
+ e.xclient.data.l[0] = _NET_WM_STATE_REMOVE;
+ e.xclient.data.l[1] = data->videodata->_NET_WM_STATE_MAXIMIZED_VERT;
+ e.xclient.data.l[2] = data->videodata->_NET_WM_STATE_MAXIMIZED_HORZ;
+ e.xclient.data.l[3] = 0l;
+ X11_XSendEvent(display, RootWindow(display, displaydata->screen), 0,
+ SubstructureNotifyMask | SubstructureRedirectMask, &e);
+ }
} else {
Uint32 flags;
Display *display = data->videodata->display;
SDL_bool oldstyle_fullscreen;
SDL_bool grab_keyboard;
- const char *hint;
/* ICCCM2.0-compliant window managers can handle fullscreen windows
If we're using XVidMode to change resolution we need to confine
if (oldstyle_fullscreen || grabbed) {
/* Try to grab the mouse */
- for (;;) {
- int result =
- X11_XGrabPointer(display, data->xwindow, True, 0, GrabModeAsync,
- GrabModeAsync, data->xwindow, None, CurrentTime);
- if (result == GrabSuccess) {
- break;
+ if (!data->videodata->broken_pointer_grab) {
+ const unsigned int mask = ButtonPressMask | ButtonReleaseMask | PointerMotionMask | FocusChangeMask;
+ int attempts;
+ int result;
+
+ /* Try for up to 5000ms (5s) to grab. If it still fails, stop trying. */
+ for (attempts = 0; attempts < 100; attempts++) {
+ result = X11_XGrabPointer(display, data->xwindow, True, mask, GrabModeAsync,
+ GrabModeAsync, data->xwindow, None, CurrentTime);
+ if (result == GrabSuccess) {
+ break;
+ }
+ SDL_Delay(50);
+ }
+
+ if (result != GrabSuccess) {
+ SDL_LogWarn(SDL_LOG_CATEGORY_VIDEO, "The X server refused to let us grab the mouse. You might experience input bugs.");
+ data->videodata->broken_pointer_grab = SDL_TRUE; /* don't try again. */
}
- SDL_Delay(50);
}
/* Raise the window if we grab the mouse */
X11_XRaiseWindow(display, data->xwindow);
/* Now grab the keyboard */
- hint = SDL_GetHint(SDL_HINT_GRAB_KEYBOARD);
- if (hint && SDL_atoi(hint)) {
+ if (SDL_GetHintBoolean(SDL_HINT_GRAB_KEYBOARD, SDL_FALSE)) {
grab_keyboard = SDL_TRUE;
} else {
/* We need to do this with the old style override_redirect
info->info.x11.window = data->xwindow;
return SDL_TRUE;
} else {
- SDL_SetError("Application not compiled with SDL %d.%d\n",
+ SDL_SetError("Application not compiled with SDL %d.%d",
SDL_MAJOR_VERSION, SDL_MINOR_VERSION);
return SDL_FALSE;
}