+++ /dev/null
-//========================================================================
-// GLFW 3.4 X11 - www.glfw.org
-//------------------------------------------------------------------------
-// Copyright (c) 2002-2006 Marcus Geelnard
-// Copyright (c) 2006-2019 Camilla Löwy <elmindreda@glfw.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
-// arising from the use of this software.
-//
-// Permission is granted to anyone to use this software for any purpose,
-// including commercial applications, and to alter it and redistribute it
-// freely, subject to the following restrictions:
-//
-// 1. The origin of this software must not be misrepresented; you must not
-// claim that you wrote the original software. If you use this software
-// in a product, an acknowledgment in the product documentation would
-// be appreciated but is not required.
-//
-// 2. Altered source versions must be plainly marked as such, and must not
-// be misrepresented as being the original software.
-//
-// 3. This notice may not be removed or altered from any source
-// distribution.
-//
-//========================================================================
-// It is fine to use C99 in this file because it will not be built with VS
-//========================================================================
-
-#include "internal.h"
-
-#include <X11/cursorfont.h>
-#include <X11/Xmd.h>
-
-#include <sys/select.h>
-
-#include <string.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <limits.h>
-#include <errno.h>
-#include <assert.h>
-
-// Action for EWMH client messages
-#define _NET_WM_STATE_REMOVE 0
-#define _NET_WM_STATE_ADD 1
-#define _NET_WM_STATE_TOGGLE 2
-
-// Additional mouse button names for XButtonEvent
-#define Button6 6
-#define Button7 7
-
-// Motif WM hints flags
-#define MWM_HINTS_DECORATIONS 2
-#define MWM_DECOR_ALL 1
-
-#define _GLFW_XDND_VERSION 5
-
-
-// Wait for data to arrive using select
-// This avoids blocking other threads via the per-display Xlib lock that also
-// covers GLX functions
-//
-static GLFWbool waitForEvent(double* timeout)
-{
- fd_set fds;
- const int fd = ConnectionNumber(_glfw.x11.display);
- int count = fd + 1;
-
-#if defined(__linux__)
- if (_glfw.linjs.inotify > fd)
- count = _glfw.linjs.inotify + 1;
-#endif
- for (;;)
- {
- FD_ZERO(&fds);
- FD_SET(fd, &fds);
-#if defined(__linux__)
- if (_glfw.linjs.inotify > 0)
- FD_SET(_glfw.linjs.inotify, &fds);
-#endif
-
- if (timeout)
- {
- const long seconds = (long) *timeout;
- const long microseconds = (long) ((*timeout - seconds) * 1e6);
- struct timeval tv = { seconds, microseconds };
- const uint64_t base = _glfwPlatformGetTimerValue();
-
- const int result = select(count, &fds, NULL, NULL, &tv);
- const int error = errno;
-
- *timeout -= (_glfwPlatformGetTimerValue() - base) /
- (double) _glfwPlatformGetTimerFrequency();
-
- if (result > 0)
- return GLFW_TRUE;
- if ((result == -1 && error == EINTR) || *timeout <= 0.0)
- return GLFW_FALSE;
- }
- else if (select(count, &fds, NULL, NULL, NULL) != -1 || errno != EINTR)
- return GLFW_TRUE;
- }
-}
-
-// Waits until a VisibilityNotify event arrives for the specified window or the
-// timeout period elapses (ICCCM section 4.2.2)
-//
-static GLFWbool waitForVisibilityNotify(_GLFWwindow* window)
-{
- XEvent dummy;
- double timeout = 0.1;
-
- while (!XCheckTypedWindowEvent(_glfw.x11.display,
- window->x11.handle,
- VisibilityNotify,
- &dummy))
- {
- if (!waitForEvent(&timeout))
- return GLFW_FALSE;
- }
-
- return GLFW_TRUE;
-}
-
-// Returns whether the window is iconified
-//
-static int getWindowState(_GLFWwindow* window)
-{
- int result = WithdrawnState;
- struct {
- CARD32 state;
- Window icon;
- } *state = NULL;
-
- if (_glfwGetWindowPropertyX11(window->x11.handle,
- _glfw.x11.WM_STATE,
- _glfw.x11.WM_STATE,
- (unsigned char**) &state) >= 2)
- {
- result = state->state;
- }
-
- if (state)
- XFree(state);
-
- return result;
-}
-
-// Returns whether the event is a selection event
-//
-static Bool isSelectionEvent(Display* display, XEvent* event, XPointer pointer)
-{
- if (event->xany.window != _glfw.x11.helperWindowHandle)
- return False;
-
- return event->type == SelectionRequest ||
- event->type == SelectionNotify ||
- event->type == SelectionClear;
-}
-
-// Returns whether it is a _NET_FRAME_EXTENTS event for the specified window
-//
-static Bool isFrameExtentsEvent(Display* display, XEvent* event, XPointer pointer)
-{
- _GLFWwindow* window = (_GLFWwindow*) pointer;
- return event->type == PropertyNotify &&
- event->xproperty.state == PropertyNewValue &&
- event->xproperty.window == window->x11.handle &&
- event->xproperty.atom == _glfw.x11.NET_FRAME_EXTENTS;
-}
-
-// Returns whether it is a property event for the specified selection transfer
-//
-static Bool isSelPropNewValueNotify(Display* display, XEvent* event, XPointer pointer)
-{
- XEvent* notification = (XEvent*) pointer;
- return event->type == PropertyNotify &&
- event->xproperty.state == PropertyNewValue &&
- event->xproperty.window == notification->xselection.requestor &&
- event->xproperty.atom == notification->xselection.property;
-}
-
-// Translates an X event modifier state mask
-//
-static int translateState(int state)
-{
- int mods = 0;
-
- if (state & ShiftMask)
- mods |= GLFW_MOD_SHIFT;
- if (state & ControlMask)
- mods |= GLFW_MOD_CONTROL;
- if (state & Mod1Mask)
- mods |= GLFW_MOD_ALT;
- if (state & Mod4Mask)
- mods |= GLFW_MOD_SUPER;
- if (state & LockMask)
- mods |= GLFW_MOD_CAPS_LOCK;
- if (state & Mod2Mask)
- mods |= GLFW_MOD_NUM_LOCK;
-
- return mods;
-}
-
-// Translates an X11 key code to a GLFW key token
-//
-static int translateKey(int scancode)
-{
- // Use the pre-filled LUT (see createKeyTables() in x11_init.c)
- if (scancode < 0 || scancode > 255)
- return GLFW_KEY_UNKNOWN;
-
- return _glfw.x11.keycodes[scancode];
-}
-
-// Sends an EWMH or ICCCM event to the window manager
-//
-static void sendEventToWM(_GLFWwindow* window, Atom type,
- long a, long b, long c, long d, long e)
-{
- XEvent event = { ClientMessage };
- event.xclient.window = window->x11.handle;
- event.xclient.format = 32; // Data is 32-bit longs
- event.xclient.message_type = type;
- event.xclient.data.l[0] = a;
- event.xclient.data.l[1] = b;
- event.xclient.data.l[2] = c;
- event.xclient.data.l[3] = d;
- event.xclient.data.l[4] = e;
-
- XSendEvent(_glfw.x11.display, _glfw.x11.root,
- False,
- SubstructureNotifyMask | SubstructureRedirectMask,
- &event);
-}
-
-// Updates the normal hints according to the window settings
-//
-static void updateNormalHints(_GLFWwindow* window, int width, int height)
-{
- XSizeHints* hints = XAllocSizeHints();
-
- if (!window->monitor)
- {
- if (window->resizable)
- {
- if (window->minwidth != GLFW_DONT_CARE &&
- window->minheight != GLFW_DONT_CARE)
- {
- hints->flags |= PMinSize;
- hints->min_width = window->minwidth;
- hints->min_height = window->minheight;
- }
-
- if (window->maxwidth != GLFW_DONT_CARE &&
- window->maxheight != GLFW_DONT_CARE)
- {
- hints->flags |= PMaxSize;
- hints->max_width = window->maxwidth;
- hints->max_height = window->maxheight;
- }
-
- if (window->numer != GLFW_DONT_CARE &&
- window->denom != GLFW_DONT_CARE)
- {
- hints->flags |= PAspect;
- hints->min_aspect.x = hints->max_aspect.x = window->numer;
- hints->min_aspect.y = hints->max_aspect.y = window->denom;
- }
- }
- else
- {
- hints->flags |= (PMinSize | PMaxSize);
- hints->min_width = hints->max_width = width;
- hints->min_height = hints->max_height = height;
- }
- }
-
- hints->flags |= PWinGravity;
- hints->win_gravity = StaticGravity;
-
- XSetWMNormalHints(_glfw.x11.display, window->x11.handle, hints);
- XFree(hints);
-}
-
-// Updates the full screen status of the window
-//
-static void updateWindowMode(_GLFWwindow* window)
-{
- if (window->monitor)
- {
- if (_glfw.x11.xinerama.available &&
- _glfw.x11.NET_WM_FULLSCREEN_MONITORS)
- {
- sendEventToWM(window,
- _glfw.x11.NET_WM_FULLSCREEN_MONITORS,
- window->monitor->x11.index,
- window->monitor->x11.index,
- window->monitor->x11.index,
- window->monitor->x11.index,
- 0);
- }
-
- if (_glfw.x11.NET_WM_STATE && _glfw.x11.NET_WM_STATE_FULLSCREEN)
- {
- sendEventToWM(window,
- _glfw.x11.NET_WM_STATE,
- _NET_WM_STATE_ADD,
- _glfw.x11.NET_WM_STATE_FULLSCREEN,
- 0, 1, 0);
- }
- else
- {
- // This is the butcher's way of removing window decorations
- // Setting the override-redirect attribute on a window makes the
- // window manager ignore the window completely (ICCCM, section 4)
- // The good thing is that this makes undecorated full screen windows
- // easy to do; the bad thing is that we have to do everything
- // manually and some things (like iconify/restore) won't work at
- // all, as those are tasks usually performed by the window manager
-
- XSetWindowAttributes attributes;
- attributes.override_redirect = True;
- XChangeWindowAttributes(_glfw.x11.display,
- window->x11.handle,
- CWOverrideRedirect,
- &attributes);
-
- window->x11.overrideRedirect = GLFW_TRUE;
- }
-
- // Enable compositor bypass
- if (!window->x11.transparent)
- {
- const unsigned long value = 1;
-
- XChangeProperty(_glfw.x11.display, window->x11.handle,
- _glfw.x11.NET_WM_BYPASS_COMPOSITOR, XA_CARDINAL, 32,
- PropModeReplace, (unsigned char*) &value, 1);
- }
- }
- else
- {
- if (_glfw.x11.xinerama.available &&
- _glfw.x11.NET_WM_FULLSCREEN_MONITORS)
- {
- XDeleteProperty(_glfw.x11.display, window->x11.handle,
- _glfw.x11.NET_WM_FULLSCREEN_MONITORS);
- }
-
- if (_glfw.x11.NET_WM_STATE && _glfw.x11.NET_WM_STATE_FULLSCREEN)
- {
- sendEventToWM(window,
- _glfw.x11.NET_WM_STATE,
- _NET_WM_STATE_REMOVE,
- _glfw.x11.NET_WM_STATE_FULLSCREEN,
- 0, 1, 0);
- }
- else
- {
- XSetWindowAttributes attributes;
- attributes.override_redirect = False;
- XChangeWindowAttributes(_glfw.x11.display,
- window->x11.handle,
- CWOverrideRedirect,
- &attributes);
-
- window->x11.overrideRedirect = GLFW_FALSE;
- }
-
- // Disable compositor bypass
- if (!window->x11.transparent)
- {
- XDeleteProperty(_glfw.x11.display, window->x11.handle,
- _glfw.x11.NET_WM_BYPASS_COMPOSITOR);
- }
- }
-}
-
-// Splits and translates a text/uri-list into separate file paths
-// NOTE: This function destroys the provided string
-//
-static char** parseUriList(char* text, int* count)
-{
- const char* prefix = "file://";
- char** paths = NULL;
- char* line;
-
- *count = 0;
-
- while ((line = strtok(text, "\r\n")))
- {
- text = NULL;
-
- if (line[0] == '#')
- continue;
-
- if (strncmp(line, prefix, strlen(prefix)) == 0)
- {
- line += strlen(prefix);
- // TODO: Validate hostname
- while (*line != '/')
- line++;
- }
-
- (*count)++;
-
- char* path = calloc(strlen(line) + 1, 1);
- paths = realloc(paths, *count * sizeof(char*));
- paths[*count - 1] = path;
-
- while (*line)
- {
- if (line[0] == '%' && line[1] && line[2])
- {
- const char digits[3] = { line[1], line[2], '\0' };
- *path = strtol(digits, NULL, 16);
- line += 2;
- }
- else
- *path = *line;
-
- path++;
- line++;
- }
- }
-
- return paths;
-}
-
-// Encode a Unicode code point to a UTF-8 stream
-// Based on cutef8 by Jeff Bezanson (Public Domain)
-//
-static size_t encodeUTF8(char* s, unsigned int ch)
-{
- size_t count = 0;
-
- if (ch < 0x80)
- s[count++] = (char) ch;
- else if (ch < 0x800)
- {
- s[count++] = (ch >> 6) | 0xc0;
- s[count++] = (ch & 0x3f) | 0x80;
- }
- else if (ch < 0x10000)
- {
- s[count++] = (ch >> 12) | 0xe0;
- s[count++] = ((ch >> 6) & 0x3f) | 0x80;
- s[count++] = (ch & 0x3f) | 0x80;
- }
- else if (ch < 0x110000)
- {
- s[count++] = (ch >> 18) | 0xf0;
- s[count++] = ((ch >> 12) & 0x3f) | 0x80;
- s[count++] = ((ch >> 6) & 0x3f) | 0x80;
- s[count++] = (ch & 0x3f) | 0x80;
- }
-
- return count;
-}
-
-// Decode a Unicode code point from a UTF-8 stream
-// Based on cutef8 by Jeff Bezanson (Public Domain)
-//
-static unsigned int decodeUTF8(const char** s)
-{
- unsigned int ch = 0, count = 0;
- static const unsigned int offsets[] =
- {
- 0x00000000u, 0x00003080u, 0x000e2080u,
- 0x03c82080u, 0xfa082080u, 0x82082080u
- };
-
- do
- {
- ch = (ch << 6) + (unsigned char) **s;
- (*s)++;
- count++;
- } while ((**s & 0xc0) == 0x80);
-
- assert(count <= 6);
- return ch - offsets[count - 1];
-}
-
-// Convert the specified Latin-1 string to UTF-8
-//
-static char* convertLatin1toUTF8(const char* source)
-{
- size_t size = 1;
- const char* sp;
-
- for (sp = source; *sp; sp++)
- size += (*sp & 0x80) ? 2 : 1;
-
- char* target = calloc(size, 1);
- char* tp = target;
-
- for (sp = source; *sp; sp++)
- tp += encodeUTF8(tp, *sp);
-
- return target;
-}
-
-// Updates the cursor image according to its cursor mode
-//
-static void updateCursorImage(_GLFWwindow* window)
-{
- if (window->cursorMode == GLFW_CURSOR_NORMAL)
- {
- if (window->cursor)
- {
- XDefineCursor(_glfw.x11.display, window->x11.handle,
- window->cursor->x11.handle);
- }
- else
- XUndefineCursor(_glfw.x11.display, window->x11.handle);
- }
- else
- {
- XDefineCursor(_glfw.x11.display, window->x11.handle,
- _glfw.x11.hiddenCursorHandle);
- }
-}
-
-// Enable XI2 raw mouse motion events
-//
-static void enableRawMouseMotion(_GLFWwindow* window)
-{
- XIEventMask em;
- unsigned char mask[XIMaskLen(XI_RawMotion)] = { 0 };
-
- em.deviceid = XIAllMasterDevices;
- em.mask_len = sizeof(mask);
- em.mask = mask;
- XISetMask(mask, XI_RawMotion);
-
- XISelectEvents(_glfw.x11.display, _glfw.x11.root, &em, 1);
-}
-
-// Disable XI2 raw mouse motion events
-//
-static void disableRawMouseMotion(_GLFWwindow* window)
-{
- XIEventMask em;
- unsigned char mask[] = { 0 };
-
- em.deviceid = XIAllMasterDevices;
- em.mask_len = sizeof(mask);
- em.mask = mask;
-
- XISelectEvents(_glfw.x11.display, _glfw.x11.root, &em, 1);
-}
-
-// Apply disabled cursor mode to a focused window
-//
-static void disableCursor(_GLFWwindow* window)
-{
- if (window->rawMouseMotion)
- enableRawMouseMotion(window);
-
- _glfw.x11.disabledCursorWindow = window;
- _glfwPlatformGetCursorPos(window,
- &_glfw.x11.restoreCursorPosX,
- &_glfw.x11.restoreCursorPosY);
- updateCursorImage(window);
- _glfwCenterCursorInContentArea(window);
- XGrabPointer(_glfw.x11.display, window->x11.handle, True,
- ButtonPressMask | ButtonReleaseMask | PointerMotionMask,
- GrabModeAsync, GrabModeAsync,
- window->x11.handle,
- _glfw.x11.hiddenCursorHandle,
- CurrentTime);
-}
-
-// Exit disabled cursor mode for the specified window
-//
-static void enableCursor(_GLFWwindow* window)
-{
- if (window->rawMouseMotion)
- disableRawMouseMotion(window);
-
- _glfw.x11.disabledCursorWindow = NULL;
- XUngrabPointer(_glfw.x11.display, CurrentTime);
- _glfwPlatformSetCursorPos(window,
- _glfw.x11.restoreCursorPosX,
- _glfw.x11.restoreCursorPosY);
- updateCursorImage(window);
-}
-
-// Clear its handle when the input context has been destroyed
-//
-static void inputContextDestroyCallback(XIC ic, XPointer clientData, XPointer callData)
-{
- _GLFWwindow* window = (_GLFWwindow*) clientData;
- window->x11.ic = NULL;
-}
-
-// Create the X11 window (and its colormap)
-//
-static GLFWbool createNativeWindow(_GLFWwindow* window,
- const _GLFWwndconfig* wndconfig,
- Visual* visual, int depth)
-{
- int width = wndconfig->width;
- int height = wndconfig->height;
-
- if (wndconfig->scaleToMonitor)
- {
- width *= _glfw.x11.contentScaleX;
- height *= _glfw.x11.contentScaleY;
- }
-
- // Create a colormap based on the visual used by the current context
- window->x11.colormap = XCreateColormap(_glfw.x11.display,
- _glfw.x11.root,
- visual,
- AllocNone);
-
- window->x11.transparent = _glfwIsVisualTransparentX11(visual);
-
- XSetWindowAttributes wa = { 0 };
- wa.colormap = window->x11.colormap;
- wa.event_mask = StructureNotifyMask | KeyPressMask | KeyReleaseMask |
- PointerMotionMask | ButtonPressMask | ButtonReleaseMask |
- ExposureMask | FocusChangeMask | VisibilityChangeMask |
- EnterWindowMask | LeaveWindowMask | PropertyChangeMask;
-
- _glfwGrabErrorHandlerX11();
-
- window->x11.parent = _glfw.x11.root;
- window->x11.handle = XCreateWindow(_glfw.x11.display,
- _glfw.x11.root,
- 0, 0, // Position
- width, height,
- 0, // Border width
- depth, // Color depth
- InputOutput,
- visual,
- CWBorderPixel | CWColormap | CWEventMask,
- &wa);
-
- _glfwReleaseErrorHandlerX11();
-
- if (!window->x11.handle)
- {
- _glfwInputErrorX11(GLFW_PLATFORM_ERROR,
- "X11: Failed to create window");
- return GLFW_FALSE;
- }
-
- XSaveContext(_glfw.x11.display,
- window->x11.handle,
- _glfw.x11.context,
- (XPointer) window);
-
- if (!wndconfig->decorated)
- _glfwPlatformSetWindowDecorated(window, GLFW_FALSE);
-
- if (_glfw.x11.NET_WM_STATE && !window->monitor)
- {
- Atom states[3];
- int count = 0;
-
- if (wndconfig->floating)
- {
- if (_glfw.x11.NET_WM_STATE_ABOVE)
- states[count++] = _glfw.x11.NET_WM_STATE_ABOVE;
- }
-
- if (wndconfig->maximized)
- {
- if (_glfw.x11.NET_WM_STATE_MAXIMIZED_VERT &&
- _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ)
- {
- states[count++] = _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT;
- states[count++] = _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ;
- window->x11.maximized = GLFW_TRUE;
- }
- }
-
- if (count)
- {
- XChangeProperty(_glfw.x11.display, window->x11.handle,
- _glfw.x11.NET_WM_STATE, XA_ATOM, 32,
- PropModeReplace, (unsigned char*) states, count);
- }
- }
-
- // Declare the WM protocols supported by GLFW
- {
- Atom protocols[] =
- {
- _glfw.x11.WM_DELETE_WINDOW,
- _glfw.x11.NET_WM_PING
- };
-
- XSetWMProtocols(_glfw.x11.display, window->x11.handle,
- protocols, sizeof(protocols) / sizeof(Atom));
- }
-
- // Declare our PID
- {
- const long pid = getpid();
-
- XChangeProperty(_glfw.x11.display, window->x11.handle,
- _glfw.x11.NET_WM_PID, XA_CARDINAL, 32,
- PropModeReplace,
- (unsigned char*) &pid, 1);
- }
-
- if (_glfw.x11.NET_WM_WINDOW_TYPE && _glfw.x11.NET_WM_WINDOW_TYPE_NORMAL)
- {
- Atom type = _glfw.x11.NET_WM_WINDOW_TYPE_NORMAL;
- XChangeProperty(_glfw.x11.display, window->x11.handle,
- _glfw.x11.NET_WM_WINDOW_TYPE, XA_ATOM, 32,
- PropModeReplace, (unsigned char*) &type, 1);
- }
-
- // Set ICCCM WM_HINTS property
- {
- XWMHints* hints = XAllocWMHints();
- if (!hints)
- {
- _glfwInputError(GLFW_OUT_OF_MEMORY,
- "X11: Failed to allocate WM hints");
- return GLFW_FALSE;
- }
-
- hints->flags = StateHint;
- hints->initial_state = NormalState;
-
- XSetWMHints(_glfw.x11.display, window->x11.handle, hints);
- XFree(hints);
- }
-
- updateNormalHints(window, width, height);
-
- // Set ICCCM WM_CLASS property
- {
- XClassHint* hint = XAllocClassHint();
-
- if (strlen(wndconfig->x11.instanceName) &&
- strlen(wndconfig->x11.className))
- {
- hint->res_name = (char*) wndconfig->x11.instanceName;
- hint->res_class = (char*) wndconfig->x11.className;
- }
- else
- {
- const char* resourceName = getenv("RESOURCE_NAME");
- if (resourceName && strlen(resourceName))
- hint->res_name = (char*) resourceName;
- else if (strlen(wndconfig->title))
- hint->res_name = (char*) wndconfig->title;
- else
- hint->res_name = (char*) "glfw-application";
-
- if (strlen(wndconfig->title))
- hint->res_class = (char*) wndconfig->title;
- else
- hint->res_class = (char*) "GLFW-Application";
- }
-
- XSetClassHint(_glfw.x11.display, window->x11.handle, hint);
- XFree(hint);
- }
-
- // Announce support for Xdnd (drag and drop)
- {
- const Atom version = _GLFW_XDND_VERSION;
- XChangeProperty(_glfw.x11.display, window->x11.handle,
- _glfw.x11.XdndAware, XA_ATOM, 32,
- PropModeReplace, (unsigned char*) &version, 1);
- }
-
- if (_glfw.x11.im)
- _glfwCreateInputContextX11(window);
-
- _glfwPlatformSetWindowTitle(window, wndconfig->title);
- _glfwPlatformGetWindowPos(window, &window->x11.xpos, &window->x11.ypos);
- _glfwPlatformGetWindowSize(window, &window->x11.width, &window->x11.height);
-
- return GLFW_TRUE;
-}
-
-// Set the specified property to the selection converted to the requested target
-//
-static Atom writeTargetToProperty(const XSelectionRequestEvent* request)
-{
- int i;
- char* selectionString = NULL;
- const Atom formats[] = { _glfw.x11.UTF8_STRING, XA_STRING };
- const int formatCount = sizeof(formats) / sizeof(formats[0]);
-
- if (request->selection == _glfw.x11.PRIMARY)
- selectionString = _glfw.x11.primarySelectionString;
- else
- selectionString = _glfw.x11.clipboardString;
-
- if (request->property == None)
- {
- // The requester is a legacy client (ICCCM section 2.2)
- // We don't support legacy clients, so fail here
- return None;
- }
-
- if (request->target == _glfw.x11.TARGETS)
- {
- // The list of supported targets was requested
-
- const Atom targets[] = { _glfw.x11.TARGETS,
- _glfw.x11.MULTIPLE,
- _glfw.x11.UTF8_STRING,
- XA_STRING };
-
- XChangeProperty(_glfw.x11.display,
- request->requestor,
- request->property,
- XA_ATOM,
- 32,
- PropModeReplace,
- (unsigned char*) targets,
- sizeof(targets) / sizeof(targets[0]));
-
- return request->property;
- }
-
- if (request->target == _glfw.x11.MULTIPLE)
- {
- // Multiple conversions were requested
-
- Atom* targets;
- unsigned long i, count;
-
- count = _glfwGetWindowPropertyX11(request->requestor,
- request->property,
- _glfw.x11.ATOM_PAIR,
- (unsigned char**) &targets);
-
- for (i = 0; i < count; i += 2)
- {
- int j;
-
- for (j = 0; j < formatCount; j++)
- {
- if (targets[i] == formats[j])
- break;
- }
-
- if (j < formatCount)
- {
- XChangeProperty(_glfw.x11.display,
- request->requestor,
- targets[i + 1],
- targets[i],
- 8,
- PropModeReplace,
- (unsigned char *) selectionString,
- strlen(selectionString));
- }
- else
- targets[i + 1] = None;
- }
-
- XChangeProperty(_glfw.x11.display,
- request->requestor,
- request->property,
- _glfw.x11.ATOM_PAIR,
- 32,
- PropModeReplace,
- (unsigned char*) targets,
- count);
-
- XFree(targets);
-
- return request->property;
- }
-
- if (request->target == _glfw.x11.SAVE_TARGETS)
- {
- // The request is a check whether we support SAVE_TARGETS
- // It should be handled as a no-op side effect target
-
- XChangeProperty(_glfw.x11.display,
- request->requestor,
- request->property,
- _glfw.x11.NULL_,
- 32,
- PropModeReplace,
- NULL,
- 0);
-
- return request->property;
- }
-
- // Conversion to a data target was requested
-
- for (i = 0; i < formatCount; i++)
- {
- if (request->target == formats[i])
- {
- // The requested target is one we support
-
- XChangeProperty(_glfw.x11.display,
- request->requestor,
- request->property,
- request->target,
- 8,
- PropModeReplace,
- (unsigned char *) selectionString,
- strlen(selectionString));
-
- return request->property;
- }
- }
-
- // The requested target is not supported
-
- return None;
-}
-
-static void handleSelectionClear(XEvent* event)
-{
- if (event->xselectionclear.selection == _glfw.x11.PRIMARY)
- {
- free(_glfw.x11.primarySelectionString);
- _glfw.x11.primarySelectionString = NULL;
- }
- else
- {
- free(_glfw.x11.clipboardString);
- _glfw.x11.clipboardString = NULL;
- }
-}
-
-static void handleSelectionRequest(XEvent* event)
-{
- const XSelectionRequestEvent* request = &event->xselectionrequest;
-
- XEvent reply = { SelectionNotify };
- reply.xselection.property = writeTargetToProperty(request);
- reply.xselection.display = request->display;
- reply.xselection.requestor = request->requestor;
- reply.xselection.selection = request->selection;
- reply.xselection.target = request->target;
- reply.xselection.time = request->time;
-
- XSendEvent(_glfw.x11.display, request->requestor, False, 0, &reply);
-}
-
-static const char* getSelectionString(Atom selection)
-{
- char** selectionString = NULL;
- const Atom targets[] = { _glfw.x11.UTF8_STRING, XA_STRING };
- const size_t targetCount = sizeof(targets) / sizeof(targets[0]);
-
- if (selection == _glfw.x11.PRIMARY)
- selectionString = &_glfw.x11.primarySelectionString;
- else
- selectionString = &_glfw.x11.clipboardString;
-
- if (XGetSelectionOwner(_glfw.x11.display, selection) ==
- _glfw.x11.helperWindowHandle)
- {
- // Instead of doing a large number of X round-trips just to put this
- // string into a window property and then read it back, just return it
- return *selectionString;
- }
-
- free(*selectionString);
- *selectionString = NULL;
-
- for (size_t i = 0; i < targetCount; i++)
- {
- char* data;
- Atom actualType;
- int actualFormat;
- unsigned long itemCount, bytesAfter;
- XEvent notification, dummy;
-
- XConvertSelection(_glfw.x11.display,
- selection,
- targets[i],
- _glfw.x11.GLFW_SELECTION,
- _glfw.x11.helperWindowHandle,
- CurrentTime);
-
- while (!XCheckTypedWindowEvent(_glfw.x11.display,
- _glfw.x11.helperWindowHandle,
- SelectionNotify,
- ¬ification))
- {
- waitForEvent(NULL);
- }
-
- if (notification.xselection.property == None)
- continue;
-
- XCheckIfEvent(_glfw.x11.display,
- &dummy,
- isSelPropNewValueNotify,
- (XPointer) ¬ification);
-
- XGetWindowProperty(_glfw.x11.display,
- notification.xselection.requestor,
- notification.xselection.property,
- 0,
- LONG_MAX,
- True,
- AnyPropertyType,
- &actualType,
- &actualFormat,
- &itemCount,
- &bytesAfter,
- (unsigned char**) &data);
-
- if (actualType == _glfw.x11.INCR)
- {
- size_t size = 1;
- char* string = NULL;
-
- for (;;)
- {
- while (!XCheckIfEvent(_glfw.x11.display,
- &dummy,
- isSelPropNewValueNotify,
- (XPointer) ¬ification))
- {
- waitForEvent(NULL);
- }
-
- XFree(data);
- XGetWindowProperty(_glfw.x11.display,
- notification.xselection.requestor,
- notification.xselection.property,
- 0,
- LONG_MAX,
- True,
- AnyPropertyType,
- &actualType,
- &actualFormat,
- &itemCount,
- &bytesAfter,
- (unsigned char**) &data);
-
- if (itemCount)
- {
- size += itemCount;
- string = realloc(string, size);
- string[size - itemCount - 1] = '\0';
- strcat(string, data);
- }
-
- if (!itemCount)
- {
- if (targets[i] == XA_STRING)
- {
- *selectionString = convertLatin1toUTF8(string);
- free(string);
- }
- else
- *selectionString = string;
-
- break;
- }
- }
- }
- else if (actualType == targets[i])
- {
- if (targets[i] == XA_STRING)
- *selectionString = convertLatin1toUTF8(data);
- else
- *selectionString = _glfw_strdup(data);
- }
-
- XFree(data);
-
- if (*selectionString)
- break;
- }
-
- if (!*selectionString)
- {
- _glfwInputError(GLFW_FORMAT_UNAVAILABLE,
- "X11: Failed to convert selection to string");
- }
-
- return *selectionString;
-}
-
-// Make the specified window and its video mode active on its monitor
-//
-static void acquireMonitor(_GLFWwindow* window)
-{
- if (_glfw.x11.saver.count == 0)
- {
- // Remember old screen saver settings
- XGetScreenSaver(_glfw.x11.display,
- &_glfw.x11.saver.timeout,
- &_glfw.x11.saver.interval,
- &_glfw.x11.saver.blanking,
- &_glfw.x11.saver.exposure);
-
- // Disable screen saver
- XSetScreenSaver(_glfw.x11.display, 0, 0, DontPreferBlanking,
- DefaultExposures);
- }
-
- if (!window->monitor->window)
- _glfw.x11.saver.count++;
-
- _glfwSetVideoModeX11(window->monitor, &window->videoMode);
-
- if (window->x11.overrideRedirect)
- {
- int xpos, ypos;
- GLFWvidmode mode;
-
- // Manually position the window over its monitor
- _glfwPlatformGetMonitorPos(window->monitor, &xpos, &ypos);
- _glfwPlatformGetVideoMode(window->monitor, &mode);
-
- XMoveResizeWindow(_glfw.x11.display, window->x11.handle,
- xpos, ypos, mode.width, mode.height);
- }
-
- _glfwInputMonitorWindow(window->monitor, window);
-}
-
-// Remove the window and restore the original video mode
-//
-static void releaseMonitor(_GLFWwindow* window)
-{
- if (window->monitor->window != window)
- return;
-
- _glfwInputMonitorWindow(window->monitor, NULL);
- _glfwRestoreVideoModeX11(window->monitor);
-
- _glfw.x11.saver.count--;
-
- if (_glfw.x11.saver.count == 0)
- {
- // Restore old screen saver settings
- XSetScreenSaver(_glfw.x11.display,
- _glfw.x11.saver.timeout,
- _glfw.x11.saver.interval,
- _glfw.x11.saver.blanking,
- _glfw.x11.saver.exposure);
- }
-}
-
-// Process the specified X event
-//
-static void processEvent(XEvent *event)
-{
- int keycode = 0;
- Bool filtered = False;
-
- // HACK: Save scancode as some IMs clear the field in XFilterEvent
- if (event->type == KeyPress || event->type == KeyRelease)
- keycode = event->xkey.keycode;
-
- filtered = XFilterEvent(event, None);
-
- if (_glfw.x11.randr.available)
- {
- if (event->type == _glfw.x11.randr.eventBase + RRNotify)
- {
- XRRUpdateConfiguration(event);
- _glfwPollMonitorsX11();
- return;
- }
- }
-
- if (_glfw.x11.xkb.available)
- {
- if (event->type == _glfw.x11.xkb.eventBase + XkbEventCode)
- {
- if (((XkbEvent*) event)->any.xkb_type == XkbStateNotify &&
- (((XkbEvent*) event)->state.changed & XkbGroupStateMask))
- {
- _glfw.x11.xkb.group = ((XkbEvent*) event)->state.group;
- }
-
- return;
- }
- }
-
- if (event->type == GenericEvent)
- {
- if (_glfw.x11.xi.available)
- {
- _GLFWwindow* window = _glfw.x11.disabledCursorWindow;
-
- if (window &&
- window->rawMouseMotion &&
- event->xcookie.extension == _glfw.x11.xi.majorOpcode &&
- XGetEventData(_glfw.x11.display, &event->xcookie) &&
- event->xcookie.evtype == XI_RawMotion)
- {
- XIRawEvent* re = event->xcookie.data;
- if (re->valuators.mask_len)
- {
- const double* values = re->raw_values;
- double xpos = window->virtualCursorPosX;
- double ypos = window->virtualCursorPosY;
-
- if (XIMaskIsSet(re->valuators.mask, 0))
- {
- xpos += *values;
- values++;
- }
-
- if (XIMaskIsSet(re->valuators.mask, 1))
- ypos += *values;
-
- _glfwInputCursorPos(window, xpos, ypos);
- }
- }
-
- XFreeEventData(_glfw.x11.display, &event->xcookie);
- }
-
- return;
- }
-
- if (event->type == SelectionClear)
- {
- handleSelectionClear(event);
- return;
- }
- else if (event->type == SelectionRequest)
- {
- handleSelectionRequest(event);
- return;
- }
-
- _GLFWwindow* window = NULL;
- if (XFindContext(_glfw.x11.display,
- event->xany.window,
- _glfw.x11.context,
- (XPointer*) &window) != 0)
- {
- // This is an event for a window that has already been destroyed
- return;
- }
-
- switch (event->type)
- {
- case ReparentNotify:
- {
- window->x11.parent = event->xreparent.parent;
- return;
- }
-
- case KeyPress:
- {
- const int key = translateKey(keycode);
- const int mods = translateState(event->xkey.state);
- const int plain = !(mods & (GLFW_MOD_CONTROL | GLFW_MOD_ALT));
-
- if (window->x11.ic)
- {
- // HACK: Do not report the key press events duplicated by XIM
- // Duplicate key releases are filtered out implicitly by
- // the GLFW key repeat logic in _glfwInputKey
- // A timestamp per key is used to handle simultaneous keys
- // NOTE: Always allow the first event for each key through
- // (the server never sends a timestamp of zero)
- // NOTE: Timestamp difference is compared to handle wrap-around
- Time diff = event->xkey.time - window->x11.keyPressTimes[keycode];
- if (diff == event->xkey.time || (diff > 0 && diff < (1 << 31)))
- {
- if (keycode)
- _glfwInputKey(window, key, keycode, GLFW_PRESS, mods);
-
- window->x11.keyPressTimes[keycode] = event->xkey.time;
- }
-
- if (!filtered)
- {
- int count;
- Status status;
- char buffer[100];
- char* chars = buffer;
-
- count = Xutf8LookupString(window->x11.ic,
- &event->xkey,
- buffer, sizeof(buffer) - 1,
- NULL, &status);
-
- if (status == XBufferOverflow)
- {
- chars = calloc(count + 1, 1);
- count = Xutf8LookupString(window->x11.ic,
- &event->xkey,
- chars, count,
- NULL, &status);
- }
-
- if (status == XLookupChars || status == XLookupBoth)
- {
- const char* c = chars;
- chars[count] = '\0';
- while (c - chars < count)
- _glfwInputChar(window, decodeUTF8(&c), mods, plain);
- }
-
- if (chars != buffer)
- free(chars);
- }
- }
- else
- {
- KeySym keysym;
- XLookupString(&event->xkey, NULL, 0, &keysym, NULL);
-
- _glfwInputKey(window, key, keycode, GLFW_PRESS, mods);
-
- const long character = _glfwKeySym2Unicode(keysym);
- if (character != -1)
- _glfwInputChar(window, character, mods, plain);
- }
-
- return;
- }
-
- case KeyRelease:
- {
- const int key = translateKey(keycode);
- const int mods = translateState(event->xkey.state);
-
- if (!_glfw.x11.xkb.detectable)
- {
- // HACK: Key repeat events will arrive as KeyRelease/KeyPress
- // pairs with similar or identical time stamps
- // The key repeat logic in _glfwInputKey expects only key
- // presses to repeat, so detect and discard release events
- if (XEventsQueued(_glfw.x11.display, QueuedAfterReading))
- {
- XEvent next;
- XPeekEvent(_glfw.x11.display, &next);
-
- if (next.type == KeyPress &&
- next.xkey.window == event->xkey.window &&
- next.xkey.keycode == keycode)
- {
- // HACK: The time of repeat events sometimes doesn't
- // match that of the press event, so add an
- // epsilon
- // Toshiyuki Takahashi can press a button
- // 16 times per second so it's fairly safe to
- // assume that no human is pressing the key 50
- // times per second (value is ms)
- if ((next.xkey.time - event->xkey.time) < 20)
- {
- // This is very likely a server-generated key repeat
- // event, so ignore it
- return;
- }
- }
- }
- }
-
- _glfwInputKey(window, key, keycode, GLFW_RELEASE, mods);
- return;
- }
-
- case ButtonPress:
- {
- const int mods = translateState(event->xbutton.state);
-
- if (event->xbutton.button == Button1)
- _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_LEFT, GLFW_PRESS, mods);
- else if (event->xbutton.button == Button2)
- _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_MIDDLE, GLFW_PRESS, mods);
- else if (event->xbutton.button == Button3)
- _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_RIGHT, GLFW_PRESS, mods);
-
- // Modern X provides scroll events as mouse button presses
- else if (event->xbutton.button == Button4)
- _glfwInputScroll(window, 0.0, 1.0);
- else if (event->xbutton.button == Button5)
- _glfwInputScroll(window, 0.0, -1.0);
- else if (event->xbutton.button == Button6)
- _glfwInputScroll(window, 1.0, 0.0);
- else if (event->xbutton.button == Button7)
- _glfwInputScroll(window, -1.0, 0.0);
-
- else
- {
- // Additional buttons after 7 are treated as regular buttons
- // We subtract 4 to fill the gap left by scroll input above
- _glfwInputMouseClick(window,
- event->xbutton.button - Button1 - 4,
- GLFW_PRESS,
- mods);
- }
-
- return;
- }
-
- case ButtonRelease:
- {
- const int mods = translateState(event->xbutton.state);
-
- if (event->xbutton.button == Button1)
- {
- _glfwInputMouseClick(window,
- GLFW_MOUSE_BUTTON_LEFT,
- GLFW_RELEASE,
- mods);
- }
- else if (event->xbutton.button == Button2)
- {
- _glfwInputMouseClick(window,
- GLFW_MOUSE_BUTTON_MIDDLE,
- GLFW_RELEASE,
- mods);
- }
- else if (event->xbutton.button == Button3)
- {
- _glfwInputMouseClick(window,
- GLFW_MOUSE_BUTTON_RIGHT,
- GLFW_RELEASE,
- mods);
- }
- else if (event->xbutton.button > Button7)
- {
- // Additional buttons after 7 are treated as regular buttons
- // We subtract 4 to fill the gap left by scroll input above
- _glfwInputMouseClick(window,
- event->xbutton.button - Button1 - 4,
- GLFW_RELEASE,
- mods);
- }
-
- return;
- }
-
- case EnterNotify:
- {
- // XEnterWindowEvent is XCrossingEvent
- const int x = event->xcrossing.x;
- const int y = event->xcrossing.y;
-
- // HACK: This is a workaround for WMs (KWM, Fluxbox) that otherwise
- // ignore the defined cursor for hidden cursor mode
- if (window->cursorMode == GLFW_CURSOR_HIDDEN)
- updateCursorImage(window);
-
- _glfwInputCursorEnter(window, GLFW_TRUE);
- _glfwInputCursorPos(window, x, y);
-
- window->x11.lastCursorPosX = x;
- window->x11.lastCursorPosY = y;
- return;
- }
-
- case LeaveNotify:
- {
- _glfwInputCursorEnter(window, GLFW_FALSE);
- return;
- }
-
- case MotionNotify:
- {
- const int x = event->xmotion.x;
- const int y = event->xmotion.y;
-
- if (x != window->x11.warpCursorPosX ||
- y != window->x11.warpCursorPosY)
- {
- // The cursor was moved by something other than GLFW
-
- if (window->cursorMode == GLFW_CURSOR_DISABLED)
- {
- if (_glfw.x11.disabledCursorWindow != window)
- return;
- if (window->rawMouseMotion)
- return;
-
- const int dx = x - window->x11.lastCursorPosX;
- const int dy = y - window->x11.lastCursorPosY;
-
- _glfwInputCursorPos(window,
- window->virtualCursorPosX + dx,
- window->virtualCursorPosY + dy);
- }
- else
- _glfwInputCursorPos(window, x, y);
- }
-
- window->x11.lastCursorPosX = x;
- window->x11.lastCursorPosY = y;
- return;
- }
-
- case ConfigureNotify:
- {
- if (event->xconfigure.width != window->x11.width ||
- event->xconfigure.height != window->x11.height)
- {
- _glfwInputFramebufferSize(window,
- event->xconfigure.width,
- event->xconfigure.height);
-
- _glfwInputWindowSize(window,
- event->xconfigure.width,
- event->xconfigure.height);
-
- window->x11.width = event->xconfigure.width;
- window->x11.height = event->xconfigure.height;
- }
-
- int xpos = event->xconfigure.x;
- int ypos = event->xconfigure.y;
-
- // NOTE: ConfigureNotify events from the server are in local
- // coordinates, so if we are reparented we need to translate
- // the position into root (screen) coordinates
- if (!event->xany.send_event && window->x11.parent != _glfw.x11.root)
- {
- _glfwGrabErrorHandlerX11();
-
- Window dummy;
- XTranslateCoordinates(_glfw.x11.display,
- window->x11.parent,
- _glfw.x11.root,
- xpos, ypos,
- &xpos, &ypos,
- &dummy);
-
- _glfwReleaseErrorHandlerX11();
- if (_glfw.x11.errorCode == BadWindow)
- return;
- }
-
- if (xpos != window->x11.xpos || ypos != window->x11.ypos)
- {
- _glfwInputWindowPos(window, xpos, ypos);
- window->x11.xpos = xpos;
- window->x11.ypos = ypos;
- }
-
- return;
- }
-
- case ClientMessage:
- {
- // Custom client message, probably from the window manager
-
- if (filtered)
- return;
-
- if (event->xclient.message_type == None)
- return;
-
- if (event->xclient.message_type == _glfw.x11.WM_PROTOCOLS)
- {
- const Atom protocol = event->xclient.data.l[0];
- if (protocol == None)
- return;
-
- if (protocol == _glfw.x11.WM_DELETE_WINDOW)
- {
- // The window manager was asked to close the window, for
- // example by the user pressing a 'close' window decoration
- // button
- _glfwInputWindowCloseRequest(window);
- }
- else if (protocol == _glfw.x11.NET_WM_PING)
- {
- // The window manager is pinging the application to ensure
- // it's still responding to events
-
- XEvent reply = *event;
- reply.xclient.window = _glfw.x11.root;
-
- XSendEvent(_glfw.x11.display, _glfw.x11.root,
- False,
- SubstructureNotifyMask | SubstructureRedirectMask,
- &reply);
- }
- }
- else if (event->xclient.message_type == _glfw.x11.XdndEnter)
- {
- // A drag operation has entered the window
- unsigned long i, count;
- Atom* formats = NULL;
- const GLFWbool list = event->xclient.data.l[1] & 1;
-
- _glfw.x11.xdnd.source = event->xclient.data.l[0];
- _glfw.x11.xdnd.version = event->xclient.data.l[1] >> 24;
- _glfw.x11.xdnd.format = None;
-
- if (_glfw.x11.xdnd.version > _GLFW_XDND_VERSION)
- return;
-
- if (list)
- {
- count = _glfwGetWindowPropertyX11(_glfw.x11.xdnd.source,
- _glfw.x11.XdndTypeList,
- XA_ATOM,
- (unsigned char**) &formats);
- }
- else
- {
- count = 3;
- formats = (Atom*) event->xclient.data.l + 2;
- }
-
- for (i = 0; i < count; i++)
- {
- if (formats[i] == _glfw.x11.text_uri_list)
- {
- _glfw.x11.xdnd.format = _glfw.x11.text_uri_list;
- break;
- }
- }
-
- if (list && formats)
- XFree(formats);
- }
- else if (event->xclient.message_type == _glfw.x11.XdndDrop)
- {
- // The drag operation has finished by dropping on the window
- Time time = CurrentTime;
-
- if (_glfw.x11.xdnd.version > _GLFW_XDND_VERSION)
- return;
-
- if (_glfw.x11.xdnd.format)
- {
- if (_glfw.x11.xdnd.version >= 1)
- time = event->xclient.data.l[2];
-
- // Request the chosen format from the source window
- XConvertSelection(_glfw.x11.display,
- _glfw.x11.XdndSelection,
- _glfw.x11.xdnd.format,
- _glfw.x11.XdndSelection,
- window->x11.handle,
- time);
- }
- else if (_glfw.x11.xdnd.version >= 2)
- {
- XEvent reply = { ClientMessage };
- reply.xclient.window = _glfw.x11.xdnd.source;
- reply.xclient.message_type = _glfw.x11.XdndFinished;
- reply.xclient.format = 32;
- reply.xclient.data.l[0] = window->x11.handle;
- reply.xclient.data.l[1] = 0; // The drag was rejected
- reply.xclient.data.l[2] = None;
-
- XSendEvent(_glfw.x11.display, _glfw.x11.xdnd.source,
- False, NoEventMask, &reply);
- XFlush(_glfw.x11.display);
- }
- }
- else if (event->xclient.message_type == _glfw.x11.XdndPosition)
- {
- // The drag operation has moved over the window
- const int xabs = (event->xclient.data.l[2] >> 16) & 0xffff;
- const int yabs = (event->xclient.data.l[2]) & 0xffff;
- Window dummy;
- int xpos, ypos;
-
- if (_glfw.x11.xdnd.version > _GLFW_XDND_VERSION)
- return;
-
- XTranslateCoordinates(_glfw.x11.display,
- _glfw.x11.root,
- window->x11.handle,
- xabs, yabs,
- &xpos, &ypos,
- &dummy);
-
- _glfwInputCursorPos(window, xpos, ypos);
-
- XEvent reply = { ClientMessage };
- reply.xclient.window = _glfw.x11.xdnd.source;
- reply.xclient.message_type = _glfw.x11.XdndStatus;
- reply.xclient.format = 32;
- reply.xclient.data.l[0] = window->x11.handle;
- reply.xclient.data.l[2] = 0; // Specify an empty rectangle
- reply.xclient.data.l[3] = 0;
-
- if (_glfw.x11.xdnd.format)
- {
- // Reply that we are ready to copy the dragged data
- reply.xclient.data.l[1] = 1; // Accept with no rectangle
- if (_glfw.x11.xdnd.version >= 2)
- reply.xclient.data.l[4] = _glfw.x11.XdndActionCopy;
- }
-
- XSendEvent(_glfw.x11.display, _glfw.x11.xdnd.source,
- False, NoEventMask, &reply);
- XFlush(_glfw.x11.display);
- }
-
- return;
- }
-
- case SelectionNotify:
- {
- if (event->xselection.property == _glfw.x11.XdndSelection)
- {
- // The converted data from the drag operation has arrived
- char* data;
- const unsigned long result =
- _glfwGetWindowPropertyX11(event->xselection.requestor,
- event->xselection.property,
- event->xselection.target,
- (unsigned char**) &data);
-
- if (result)
- {
- int i, count;
- char** paths = parseUriList(data, &count);
-
- _glfwInputDrop(window, count, (const char**) paths);
-
- for (i = 0; i < count; i++)
- free(paths[i]);
- free(paths);
- }
-
- if (data)
- XFree(data);
-
- if (_glfw.x11.xdnd.version >= 2)
- {
- XEvent reply = { ClientMessage };
- reply.xclient.window = _glfw.x11.xdnd.source;
- reply.xclient.message_type = _glfw.x11.XdndFinished;
- reply.xclient.format = 32;
- reply.xclient.data.l[0] = window->x11.handle;
- reply.xclient.data.l[1] = result;
- reply.xclient.data.l[2] = _glfw.x11.XdndActionCopy;
-
- XSendEvent(_glfw.x11.display, _glfw.x11.xdnd.source,
- False, NoEventMask, &reply);
- XFlush(_glfw.x11.display);
- }
- }
-
- return;
- }
-
- case FocusIn:
- {
- if (event->xfocus.mode == NotifyGrab ||
- event->xfocus.mode == NotifyUngrab)
- {
- // Ignore focus events from popup indicator windows, window menu
- // key chords and window dragging
- return;
- }
-
- if (window->cursorMode == GLFW_CURSOR_DISABLED)
- disableCursor(window);
-
- if (window->x11.ic)
- XSetICFocus(window->x11.ic);
-
- _glfwInputWindowFocus(window, GLFW_TRUE);
- return;
- }
-
- case FocusOut:
- {
- if (event->xfocus.mode == NotifyGrab ||
- event->xfocus.mode == NotifyUngrab)
- {
- // Ignore focus events from popup indicator windows, window menu
- // key chords and window dragging
- return;
- }
-
- if (window->cursorMode == GLFW_CURSOR_DISABLED)
- enableCursor(window);
-
- if (window->x11.ic)
- XUnsetICFocus(window->x11.ic);
-
- if (window->monitor && window->autoIconify)
- _glfwPlatformIconifyWindow(window);
-
- _glfwInputWindowFocus(window, GLFW_FALSE);
- return;
- }
-
- case Expose:
- {
- _glfwInputWindowDamage(window);
- return;
- }
-
- case PropertyNotify:
- {
- if (event->xproperty.state != PropertyNewValue)
- return;
-
- if (event->xproperty.atom == _glfw.x11.WM_STATE)
- {
- const int state = getWindowState(window);
- if (state != IconicState && state != NormalState)
- return;
-
- const GLFWbool iconified = (state == IconicState);
- if (window->x11.iconified != iconified)
- {
- if (window->monitor)
- {
- if (iconified)
- releaseMonitor(window);
- else
- acquireMonitor(window);
- }
-
- window->x11.iconified = iconified;
- _glfwInputWindowIconify(window, iconified);
- }
- }
- else if (event->xproperty.atom == _glfw.x11.NET_WM_STATE)
- {
- const GLFWbool maximized = _glfwPlatformWindowMaximized(window);
- if (window->x11.maximized != maximized)
- {
- window->x11.maximized = maximized;
- _glfwInputWindowMaximize(window, maximized);
- }
- }
-
- return;
- }
-
- case DestroyNotify:
- return;
- }
-}
-
-
-//////////////////////////////////////////////////////////////////////////
-////// GLFW internal API //////
-//////////////////////////////////////////////////////////////////////////
-
-// Retrieve a single window property of the specified type
-// Inspired by fghGetWindowProperty from freeglut
-//
-unsigned long _glfwGetWindowPropertyX11(Window window,
- Atom property,
- Atom type,
- unsigned char** value)
-{
- Atom actualType;
- int actualFormat;
- unsigned long itemCount, bytesAfter;
-
- XGetWindowProperty(_glfw.x11.display,
- window,
- property,
- 0,
- LONG_MAX,
- False,
- type,
- &actualType,
- &actualFormat,
- &itemCount,
- &bytesAfter,
- value);
-
- return itemCount;
-}
-
-GLFWbool _glfwIsVisualTransparentX11(Visual* visual)
-{
- if (!_glfw.x11.xrender.available)
- return GLFW_FALSE;
-
- XRenderPictFormat* pf = XRenderFindVisualFormat(_glfw.x11.display, visual);
- return pf && pf->direct.alphaMask;
-}
-
-// Push contents of our selection to clipboard manager
-//
-void _glfwPushSelectionToManagerX11(void)
-{
- XConvertSelection(_glfw.x11.display,
- _glfw.x11.CLIPBOARD_MANAGER,
- _glfw.x11.SAVE_TARGETS,
- None,
- _glfw.x11.helperWindowHandle,
- CurrentTime);
-
- for (;;)
- {
- XEvent event;
-
- while (XCheckIfEvent(_glfw.x11.display, &event, isSelectionEvent, NULL))
- {
- switch (event.type)
- {
- case SelectionRequest:
- handleSelectionRequest(&event);
- break;
-
- case SelectionClear:
- handleSelectionClear(&event);
- break;
-
- case SelectionNotify:
- {
- if (event.xselection.target == _glfw.x11.SAVE_TARGETS)
- {
- // This means one of two things; either the selection
- // was not owned, which means there is no clipboard
- // manager, or the transfer to the clipboard manager has
- // completed
- // In either case, it means we are done here
- return;
- }
-
- break;
- }
- }
- }
-
- waitForEvent(NULL);
- }
-}
-
-void _glfwCreateInputContextX11(_GLFWwindow* window)
-{
- XIMCallback callback;
- callback.callback = (XIMProc) inputContextDestroyCallback;
- callback.client_data = (XPointer) window;
-
- window->x11.ic = XCreateIC(_glfw.x11.im,
- XNInputStyle,
- XIMPreeditNothing | XIMStatusNothing,
- XNClientWindow,
- window->x11.handle,
- XNFocusWindow,
- window->x11.handle,
- XNDestroyCallback,
- &callback,
- NULL);
-
- if (window->x11.ic)
- {
- XWindowAttributes attribs;
- XGetWindowAttributes(_glfw.x11.display, window->x11.handle, &attribs);
-
- unsigned long filter = 0;
- if (XGetICValues(window->x11.ic, XNFilterEvents, &filter, NULL) == NULL)
- {
- XSelectInput(_glfw.x11.display,
- window->x11.handle,
- attribs.your_event_mask | filter);
- }
- }
-}
-
-
-//////////////////////////////////////////////////////////////////////////
-////// GLFW platform API //////
-//////////////////////////////////////////////////////////////////////////
-
-int _glfwPlatformCreateWindow(_GLFWwindow* window,
- const _GLFWwndconfig* wndconfig,
- const _GLFWctxconfig* ctxconfig,
- const _GLFWfbconfig* fbconfig)
-{
- Visual* visual = NULL;
- int depth;
-
- if (ctxconfig->client != GLFW_NO_API)
- {
- if (ctxconfig->source == GLFW_NATIVE_CONTEXT_API)
- {
- if (!_glfwInitGLX())
- return GLFW_FALSE;
- if (!_glfwChooseVisualGLX(wndconfig, ctxconfig, fbconfig, &visual, &depth))
- return GLFW_FALSE;
- }
- else if (ctxconfig->source == GLFW_EGL_CONTEXT_API)
- {
- if (!_glfwInitEGL())
- return GLFW_FALSE;
- if (!_glfwChooseVisualEGL(wndconfig, ctxconfig, fbconfig, &visual, &depth))
- return GLFW_FALSE;
- }
- else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API)
- {
- if (!_glfwInitOSMesa())
- return GLFW_FALSE;
- }
- }
-
- if (!visual)
- {
- visual = DefaultVisual(_glfw.x11.display, _glfw.x11.screen);
- depth = DefaultDepth(_glfw.x11.display, _glfw.x11.screen);
- }
-
- if (!createNativeWindow(window, wndconfig, visual, depth))
- return GLFW_FALSE;
-
- if (ctxconfig->client != GLFW_NO_API)
- {
- if (ctxconfig->source == GLFW_NATIVE_CONTEXT_API)
- {
- if (!_glfwCreateContextGLX(window, ctxconfig, fbconfig))
- return GLFW_FALSE;
- }
- else if (ctxconfig->source == GLFW_EGL_CONTEXT_API)
- {
- if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig))
- return GLFW_FALSE;
- }
- else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API)
- {
- if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig))
- return GLFW_FALSE;
- }
- }
-
- if (window->monitor)
- {
- _glfwPlatformShowWindow(window);
- updateWindowMode(window);
- acquireMonitor(window);
- }
-
- XFlush(_glfw.x11.display);
- return GLFW_TRUE;
-}
-
-void _glfwPlatformDestroyWindow(_GLFWwindow* window)
-{
- if (_glfw.x11.disabledCursorWindow == window)
- _glfw.x11.disabledCursorWindow = NULL;
-
- if (window->monitor)
- releaseMonitor(window);
-
- if (window->x11.ic)
- {
- XDestroyIC(window->x11.ic);
- window->x11.ic = NULL;
- }
-
- if (window->context.destroy)
- window->context.destroy(window);
-
- if (window->x11.handle)
- {
- XDeleteContext(_glfw.x11.display, window->x11.handle, _glfw.x11.context);
- XUnmapWindow(_glfw.x11.display, window->x11.handle);
- XDestroyWindow(_glfw.x11.display, window->x11.handle);
- window->x11.handle = (Window) 0;
- }
-
- if (window->x11.colormap)
- {
- XFreeColormap(_glfw.x11.display, window->x11.colormap);
- window->x11.colormap = (Colormap) 0;
- }
-
- XFlush(_glfw.x11.display);
-}
-
-void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title)
-{
- if (_glfw.x11.xlib.utf8)
- {
- Xutf8SetWMProperties(_glfw.x11.display,
- window->x11.handle,
- title, title,
- NULL, 0,
- NULL, NULL, NULL);
- }
-
- XChangeProperty(_glfw.x11.display, window->x11.handle,
- _glfw.x11.NET_WM_NAME, _glfw.x11.UTF8_STRING, 8,
- PropModeReplace,
- (unsigned char*) title, strlen(title));
-
- XChangeProperty(_glfw.x11.display, window->x11.handle,
- _glfw.x11.NET_WM_ICON_NAME, _glfw.x11.UTF8_STRING, 8,
- PropModeReplace,
- (unsigned char*) title, strlen(title));
-
- XFlush(_glfw.x11.display);
-}
-
-void _glfwPlatformSetWindowIcon(_GLFWwindow* window,
- int count, const GLFWimage* images)
-{
- if (count)
- {
- int i, j, longCount = 0;
-
- for (i = 0; i < count; i++)
- longCount += 2 + images[i].width * images[i].height;
-
- long* icon = calloc(longCount, sizeof(long));
- long* target = icon;
-
- for (i = 0; i < count; i++)
- {
- *target++ = images[i].width;
- *target++ = images[i].height;
-
- for (j = 0; j < images[i].width * images[i].height; j++)
- {
- *target++ = (images[i].pixels[j * 4 + 0] << 16) |
- (images[i].pixels[j * 4 + 1] << 8) |
- (images[i].pixels[j * 4 + 2] << 0) |
- (images[i].pixels[j * 4 + 3] << 24);
- }
- }
-
- XChangeProperty(_glfw.x11.display, window->x11.handle,
- _glfw.x11.NET_WM_ICON,
- XA_CARDINAL, 32,
- PropModeReplace,
- (unsigned char*) icon,
- longCount);
-
- free(icon);
- }
- else
- {
- XDeleteProperty(_glfw.x11.display, window->x11.handle,
- _glfw.x11.NET_WM_ICON);
- }
-
- XFlush(_glfw.x11.display);
-}
-
-void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos)
-{
- Window dummy;
- int x, y;
-
- XTranslateCoordinates(_glfw.x11.display, window->x11.handle, _glfw.x11.root,
- 0, 0, &x, &y, &dummy);
-
- if (xpos)
- *xpos = x;
- if (ypos)
- *ypos = y;
-}
-
-void _glfwPlatformSetWindowPos(_GLFWwindow* window, int xpos, int ypos)
-{
- // HACK: Explicitly setting PPosition to any value causes some WMs, notably
- // Compiz and Metacity, to honor the position of unmapped windows
- if (!_glfwPlatformWindowVisible(window))
- {
- long supplied;
- XSizeHints* hints = XAllocSizeHints();
-
- if (XGetWMNormalHints(_glfw.x11.display, window->x11.handle, hints, &supplied))
- {
- hints->flags |= PPosition;
- hints->x = hints->y = 0;
-
- XSetWMNormalHints(_glfw.x11.display, window->x11.handle, hints);
- }
-
- XFree(hints);
- }
-
- XMoveWindow(_glfw.x11.display, window->x11.handle, xpos, ypos);
- XFlush(_glfw.x11.display);
-}
-
-void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height)
-{
- XWindowAttributes attribs;
- XGetWindowAttributes(_glfw.x11.display, window->x11.handle, &attribs);
-
- if (width)
- *width = attribs.width;
- if (height)
- *height = attribs.height;
-}
-
-void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height)
-{
- if (window->monitor)
- {
- if (window->monitor->window == window)
- acquireMonitor(window);
- }
- else
- {
- if (!window->resizable)
- updateNormalHints(window, width, height);
-
- XResizeWindow(_glfw.x11.display, window->x11.handle, width, height);
- }
-
- XFlush(_glfw.x11.display);
-}
-
-void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window,
- int minwidth, int minheight,
- int maxwidth, int maxheight)
-{
- int width, height;
- _glfwPlatformGetWindowSize(window, &width, &height);
- updateNormalHints(window, width, height);
- XFlush(_glfw.x11.display);
-}
-
-void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int numer, int denom)
-{
- int width, height;
- _glfwPlatformGetWindowSize(window, &width, &height);
- updateNormalHints(window, width, height);
- XFlush(_glfw.x11.display);
-}
-
-void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height)
-{
- _glfwPlatformGetWindowSize(window, width, height);
-}
-
-void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window,
- int* left, int* top,
- int* right, int* bottom)
-{
- long* extents = NULL;
-
- if (window->monitor || !window->decorated)
- return;
-
- if (_glfw.x11.NET_FRAME_EXTENTS == None)
- return;
-
- if (!_glfwPlatformWindowVisible(window) &&
- _glfw.x11.NET_REQUEST_FRAME_EXTENTS)
- {
- XEvent event;
- double timeout = 0.5;
-
- // Ensure _NET_FRAME_EXTENTS is set, allowing glfwGetWindowFrameSize to
- // function before the window is mapped
- sendEventToWM(window, _glfw.x11.NET_REQUEST_FRAME_EXTENTS,
- 0, 0, 0, 0, 0);
-
- // HACK: Use a timeout because earlier versions of some window managers
- // (at least Unity, Fluxbox and Xfwm) failed to send the reply
- // They have been fixed but broken versions are still in the wild
- // If you are affected by this and your window manager is NOT
- // listed above, PLEASE report it to their and our issue trackers
- while (!XCheckIfEvent(_glfw.x11.display,
- &event,
- isFrameExtentsEvent,
- (XPointer) window))
- {
- if (!waitForEvent(&timeout))
- {
- _glfwInputError(GLFW_PLATFORM_ERROR,
- "X11: The window manager has a broken _NET_REQUEST_FRAME_EXTENTS implementation; please report this issue");
- return;
- }
- }
- }
-
- if (_glfwGetWindowPropertyX11(window->x11.handle,
- _glfw.x11.NET_FRAME_EXTENTS,
- XA_CARDINAL,
- (unsigned char**) &extents) == 4)
- {
- if (left)
- *left = extents[0];
- if (top)
- *top = extents[2];
- if (right)
- *right = extents[1];
- if (bottom)
- *bottom = extents[3];
- }
-
- if (extents)
- XFree(extents);
-}
-
-void _glfwPlatformGetWindowContentScale(_GLFWwindow* window,
- float* xscale, float* yscale)
-{
- if (xscale)
- *xscale = _glfw.x11.contentScaleX;
- if (yscale)
- *yscale = _glfw.x11.contentScaleY;
-}
-
-void _glfwPlatformIconifyWindow(_GLFWwindow* window)
-{
- if (window->x11.overrideRedirect)
- {
- // Override-redirect windows cannot be iconified or restored, as those
- // tasks are performed by the window manager
- _glfwInputError(GLFW_PLATFORM_ERROR,
- "X11: Iconification of full screen windows requires a WM that supports EWMH full screen");
- return;
- }
-
- XIconifyWindow(_glfw.x11.display, window->x11.handle, _glfw.x11.screen);
- XFlush(_glfw.x11.display);
-}
-
-void _glfwPlatformRestoreWindow(_GLFWwindow* window)
-{
- if (window->x11.overrideRedirect)
- {
- // Override-redirect windows cannot be iconified or restored, as those
- // tasks are performed by the window manager
- _glfwInputError(GLFW_PLATFORM_ERROR,
- "X11: Iconification of full screen windows requires a WM that supports EWMH full screen");
- return;
- }
-
- if (_glfwPlatformWindowIconified(window))
- {
- XMapWindow(_glfw.x11.display, window->x11.handle);
- waitForVisibilityNotify(window);
- }
- else if (_glfwPlatformWindowVisible(window))
- {
- if (_glfw.x11.NET_WM_STATE &&
- _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT &&
- _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ)
- {
- sendEventToWM(window,
- _glfw.x11.NET_WM_STATE,
- _NET_WM_STATE_REMOVE,
- _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT,
- _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ,
- 1, 0);
- }
- }
-
- XFlush(_glfw.x11.display);
-}
-
-void _glfwPlatformMaximizeWindow(_GLFWwindow* window)
-{
- if (!_glfw.x11.NET_WM_STATE ||
- !_glfw.x11.NET_WM_STATE_MAXIMIZED_VERT ||
- !_glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ)
- {
- return;
- }
-
- if (_glfwPlatformWindowVisible(window))
- {
- sendEventToWM(window,
- _glfw.x11.NET_WM_STATE,
- _NET_WM_STATE_ADD,
- _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT,
- _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ,
- 1, 0);
- }
- else
- {
- Atom* states = NULL;
- unsigned long count =
- _glfwGetWindowPropertyX11(window->x11.handle,
- _glfw.x11.NET_WM_STATE,
- XA_ATOM,
- (unsigned char**) &states);
-
- // NOTE: We don't check for failure as this property may not exist yet
- // and that's fine (and we'll create it implicitly with append)
-
- Atom missing[2] =
- {
- _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT,
- _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ
- };
- unsigned long missingCount = 2;
-
- for (unsigned long i = 0; i < count; i++)
- {
- for (unsigned long j = 0; j < missingCount; j++)
- {
- if (states[i] == missing[j])
- {
- missing[j] = missing[missingCount - 1];
- missingCount--;
- }
- }
- }
-
- if (states)
- XFree(states);
-
- if (!missingCount)
- return;
-
- XChangeProperty(_glfw.x11.display, window->x11.handle,
- _glfw.x11.NET_WM_STATE, XA_ATOM, 32,
- PropModeAppend,
- (unsigned char*) missing,
- missingCount);
- }
-
- XFlush(_glfw.x11.display);
-}
-
-void _glfwPlatformShowWindow(_GLFWwindow* window)
-{
- if (_glfwPlatformWindowVisible(window))
- return;
-
- XMapWindow(_glfw.x11.display, window->x11.handle);
- waitForVisibilityNotify(window);
-}
-
-void _glfwPlatformHideWindow(_GLFWwindow* window)
-{
- XUnmapWindow(_glfw.x11.display, window->x11.handle);
- XFlush(_glfw.x11.display);
-}
-
-void _glfwPlatformRequestWindowAttention(_GLFWwindow* window)
-{
- if (!_glfw.x11.NET_WM_STATE || !_glfw.x11.NET_WM_STATE_DEMANDS_ATTENTION)
- return;
-
- sendEventToWM(window,
- _glfw.x11.NET_WM_STATE,
- _NET_WM_STATE_ADD,
- _glfw.x11.NET_WM_STATE_DEMANDS_ATTENTION,
- 0, 1, 0);
-}
-
-void _glfwPlatformFocusWindow(_GLFWwindow* window)
-{
- if (_glfw.x11.NET_ACTIVE_WINDOW)
- sendEventToWM(window, _glfw.x11.NET_ACTIVE_WINDOW, 1, 0, 0, 0, 0);
- else if (_glfwPlatformWindowVisible(window))
- {
- XRaiseWindow(_glfw.x11.display, window->x11.handle);
- XSetInputFocus(_glfw.x11.display, window->x11.handle,
- RevertToParent, CurrentTime);
- }
-
- XFlush(_glfw.x11.display);
-}
-
-void _glfwPlatformSetWindowMonitor(_GLFWwindow* window,
- _GLFWmonitor* monitor,
- int xpos, int ypos,
- int width, int height,
- int refreshRate)
-{
- if (window->monitor == monitor)
- {
- if (monitor)
- {
- if (monitor->window == window)
- acquireMonitor(window);
- }
- else
- {
- if (!window->resizable)
- updateNormalHints(window, width, height);
-
- XMoveResizeWindow(_glfw.x11.display, window->x11.handle,
- xpos, ypos, width, height);
- }
-
- XFlush(_glfw.x11.display);
- return;
- }
-
- if (window->monitor)
- releaseMonitor(window);
-
- _glfwInputWindowMonitor(window, monitor);
- updateNormalHints(window, width, height);
-
- if (window->monitor)
- {
- if (!_glfwPlatformWindowVisible(window))
- {
- XMapRaised(_glfw.x11.display, window->x11.handle);
- waitForVisibilityNotify(window);
- }
-
- updateWindowMode(window);
- acquireMonitor(window);
- }
- else
- {
- updateWindowMode(window);
- XMoveResizeWindow(_glfw.x11.display, window->x11.handle,
- xpos, ypos, width, height);
- }
-
- XFlush(_glfw.x11.display);
-}
-
-int _glfwPlatformWindowFocused(_GLFWwindow* window)
-{
- Window focused;
- int state;
-
- XGetInputFocus(_glfw.x11.display, &focused, &state);
- return window->x11.handle == focused;
-}
-
-int _glfwPlatformWindowIconified(_GLFWwindow* window)
-{
- return getWindowState(window) == IconicState;
-}
-
-int _glfwPlatformWindowVisible(_GLFWwindow* window)
-{
- XWindowAttributes wa;
- XGetWindowAttributes(_glfw.x11.display, window->x11.handle, &wa);
- return wa.map_state == IsViewable;
-}
-
-int _glfwPlatformWindowMaximized(_GLFWwindow* window)
-{
- Atom* states;
- unsigned long i;
- GLFWbool maximized = GLFW_FALSE;
-
- if (!_glfw.x11.NET_WM_STATE ||
- !_glfw.x11.NET_WM_STATE_MAXIMIZED_VERT ||
- !_glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ)
- {
- return maximized;
- }
-
- const unsigned long count =
- _glfwGetWindowPropertyX11(window->x11.handle,
- _glfw.x11.NET_WM_STATE,
- XA_ATOM,
- (unsigned char**) &states);
-
- for (i = 0; i < count; i++)
- {
- if (states[i] == _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT ||
- states[i] == _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ)
- {
- maximized = GLFW_TRUE;
- break;
- }
- }
-
- if (states)
- XFree(states);
-
- return maximized;
-}
-
-int _glfwPlatformWindowHovered(_GLFWwindow* window)
-{
- Window w = _glfw.x11.root;
- while (w)
- {
- Window root;
- int rootX, rootY, childX, childY;
- unsigned int mask;
-
- _glfwGrabErrorHandlerX11();
-
- const Bool result = XQueryPointer(_glfw.x11.display, w,
- &root, &w, &rootX, &rootY,
- &childX, &childY, &mask);
-
- _glfwReleaseErrorHandlerX11();
-
- if (_glfw.x11.errorCode == BadWindow)
- w = _glfw.x11.root;
- else if (!result)
- return GLFW_FALSE;
- else if (w == window->x11.handle)
- return GLFW_TRUE;
- }
-
- return GLFW_FALSE;
-}
-
-int _glfwPlatformFramebufferTransparent(_GLFWwindow* window)
-{
- if (!window->x11.transparent)
- return GLFW_FALSE;
-
- return XGetSelectionOwner(_glfw.x11.display, _glfw.x11.NET_WM_CM_Sx) != None;
-}
-
-void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled)
-{
- int width, height;
- _glfwPlatformGetWindowSize(window, &width, &height);
- updateNormalHints(window, width, height);
-}
-
-void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled)
-{
- struct
- {
- unsigned long flags;
- unsigned long functions;
- unsigned long decorations;
- long input_mode;
- unsigned long status;
- } hints = {0};
-
- hints.flags = MWM_HINTS_DECORATIONS;
- hints.decorations = enabled ? MWM_DECOR_ALL : 0;
-
- XChangeProperty(_glfw.x11.display, window->x11.handle,
- _glfw.x11.MOTIF_WM_HINTS,
- _glfw.x11.MOTIF_WM_HINTS, 32,
- PropModeReplace,
- (unsigned char*) &hints,
- sizeof(hints) / sizeof(long));
-}
-
-void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled)
-{
- if (!_glfw.x11.NET_WM_STATE || !_glfw.x11.NET_WM_STATE_ABOVE)
- return;
-
- if (_glfwPlatformWindowVisible(window))
- {
- const long action = enabled ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE;
- sendEventToWM(window,
- _glfw.x11.NET_WM_STATE,
- action,
- _glfw.x11.NET_WM_STATE_ABOVE,
- 0, 1, 0);
- }
- else
- {
- Atom* states = NULL;
- unsigned long i, count;
-
- count = _glfwGetWindowPropertyX11(window->x11.handle,
- _glfw.x11.NET_WM_STATE,
- XA_ATOM,
- (unsigned char**) &states);
-
- // NOTE: We don't check for failure as this property may not exist yet
- // and that's fine (and we'll create it implicitly with append)
-
- if (enabled)
- {
- for (i = 0; i < count; i++)
- {
- if (states[i] == _glfw.x11.NET_WM_STATE_ABOVE)
- break;
- }
-
- if (i < count)
- return;
-
- XChangeProperty(_glfw.x11.display, window->x11.handle,
- _glfw.x11.NET_WM_STATE, XA_ATOM, 32,
- PropModeAppend,
- (unsigned char*) &_glfw.x11.NET_WM_STATE_ABOVE,
- 1);
- }
- else if (states)
- {
- for (i = 0; i < count; i++)
- {
- if (states[i] == _glfw.x11.NET_WM_STATE_ABOVE)
- break;
- }
-
- if (i == count)
- return;
-
- states[i] = states[count - 1];
- count--;
-
- XChangeProperty(_glfw.x11.display, window->x11.handle,
- _glfw.x11.NET_WM_STATE, XA_ATOM, 32,
- PropModeReplace, (unsigned char*) states, count);
- }
-
- if (states)
- XFree(states);
- }
-
- XFlush(_glfw.x11.display);
-}
-
-void _glfwPlatformSetWindowMousePassthrough(_GLFWwindow* window, GLFWbool enabled)
-{
- if (!_glfw.x11.xshape.available)
- return;
-
- if (enabled)
- {
- Region region = XCreateRegion();
- XShapeCombineRegion(_glfw.x11.display, window->x11.handle,
- ShapeInput, 0, 0, region, ShapeSet);
- XDestroyRegion(region);
- }
- else
- {
- XShapeCombineMask(_glfw.x11.display, window->x11.handle,
- ShapeInput, 0, 0, None, ShapeSet);
- }
-}
-
-float _glfwPlatformGetWindowOpacity(_GLFWwindow* window)
-{
- float opacity = 1.f;
-
- if (XGetSelectionOwner(_glfw.x11.display, _glfw.x11.NET_WM_CM_Sx))
- {
- CARD32* value = NULL;
-
- if (_glfwGetWindowPropertyX11(window->x11.handle,
- _glfw.x11.NET_WM_WINDOW_OPACITY,
- XA_CARDINAL,
- (unsigned char**) &value))
- {
- opacity = (float) (*value / (double) 0xffffffffu);
- }
-
- if (value)
- XFree(value);
- }
-
- return opacity;
-}
-
-void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity)
-{
- const CARD32 value = (CARD32) (0xffffffffu * (double) opacity);
- XChangeProperty(_glfw.x11.display, window->x11.handle,
- _glfw.x11.NET_WM_WINDOW_OPACITY, XA_CARDINAL, 32,
- PropModeReplace, (unsigned char*) &value, 1);
-}
-
-void _glfwPlatformSetRawMouseMotion(_GLFWwindow *window, GLFWbool enabled)
-{
- if (!_glfw.x11.xi.available)
- return;
-
- if (_glfw.x11.disabledCursorWindow != window)
- return;
-
- if (enabled)
- enableRawMouseMotion(window);
- else
- disableRawMouseMotion(window);
-}
-
-GLFWbool _glfwPlatformRawMouseMotionSupported(void)
-{
- return _glfw.x11.xi.available;
-}
-
-void _glfwPlatformPollEvents(void)
-{
- _GLFWwindow* window;
-
-#if defined(__linux__)
- if (_glfw.joysticksInitialized)
- _glfwDetectJoystickConnectionLinux();
-#endif
- XPending(_glfw.x11.display);
-
- while (QLength(_glfw.x11.display))
- {
- XEvent event;
- XNextEvent(_glfw.x11.display, &event);
- processEvent(&event);
- }
-
- window = _glfw.x11.disabledCursorWindow;
- if (window)
- {
- int width, height;
- _glfwPlatformGetWindowSize(window, &width, &height);
-
- // NOTE: Re-center the cursor only if it has moved since the last call,
- // to avoid breaking glfwWaitEvents with MotionNotify
- if (window->x11.lastCursorPosX != width / 2 ||
- window->x11.lastCursorPosY != height / 2)
- {
- _glfwPlatformSetCursorPos(window, width / 2, height / 2);
- }
- }
-
- XFlush(_glfw.x11.display);
-}
-
-void _glfwPlatformWaitEvents(void)
-{
- while (!XPending(_glfw.x11.display))
- waitForEvent(NULL);
-
- _glfwPlatformPollEvents();
-}
-
-void _glfwPlatformWaitEventsTimeout(double timeout)
-{
- while (!XPending(_glfw.x11.display))
- {
- if (!waitForEvent(&timeout))
- break;
- }
-
- _glfwPlatformPollEvents();
-}
-
-void _glfwPlatformPostEmptyEvent(void)
-{
- XEvent event = { ClientMessage };
- event.xclient.window = _glfw.x11.helperWindowHandle;
- event.xclient.format = 32; // Data is 32-bit longs
- event.xclient.message_type = _glfw.x11.NULL_;
-
- XSendEvent(_glfw.x11.display, _glfw.x11.helperWindowHandle, False, 0, &event);
- XFlush(_glfw.x11.display);
-}
-
-void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos)
-{
- Window root, child;
- int rootX, rootY, childX, childY;
- unsigned int mask;
-
- XQueryPointer(_glfw.x11.display, window->x11.handle,
- &root, &child,
- &rootX, &rootY, &childX, &childY,
- &mask);
-
- if (xpos)
- *xpos = childX;
- if (ypos)
- *ypos = childY;
-}
-
-void _glfwPlatformSetCursorPos(_GLFWwindow* window, double x, double y)
-{
- // Store the new position so it can be recognized later
- window->x11.warpCursorPosX = (int) x;
- window->x11.warpCursorPosY = (int) y;
-
- XWarpPointer(_glfw.x11.display, None, window->x11.handle,
- 0,0,0,0, (int) x, (int) y);
- XFlush(_glfw.x11.display);
-}
-
-void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode)
-{
- if (mode == GLFW_CURSOR_DISABLED)
- {
- if (_glfwPlatformWindowFocused(window))
- disableCursor(window);
- }
- else if (_glfw.x11.disabledCursorWindow == window)
- enableCursor(window);
- else
- updateCursorImage(window);
-
- XFlush(_glfw.x11.display);
-}
-
-const char* _glfwPlatformGetScancodeName(int scancode)
-{
- if (!_glfw.x11.xkb.available)
- return NULL;
-
- if (scancode < 0 || scancode > 0xff ||
- _glfw.x11.keycodes[scancode] == GLFW_KEY_UNKNOWN)
- {
- _glfwInputError(GLFW_INVALID_VALUE, "Invalid scancode");
- return NULL;
- }
-
- const int key = _glfw.x11.keycodes[scancode];
- const KeySym keysym = XkbKeycodeToKeysym(_glfw.x11.display,
- scancode, _glfw.x11.xkb.group, 0);
- if (keysym == NoSymbol)
- return NULL;
-
- const long ch = _glfwKeySym2Unicode(keysym);
- if (ch == -1)
- return NULL;
-
- const size_t count = encodeUTF8(_glfw.x11.keynames[key], (unsigned int) ch);
- if (count == 0)
- return NULL;
-
- _glfw.x11.keynames[key][count] = '\0';
- return _glfw.x11.keynames[key];
-}
-
-int _glfwPlatformGetKeyScancode(int key)
-{
- return _glfw.x11.scancodes[key];
-}
-
-int _glfwPlatformCreateCursor(_GLFWcursor* cursor,
- const GLFWimage* image,
- int xhot, int yhot)
-{
- cursor->x11.handle = _glfwCreateCursorX11(image, xhot, yhot);
- if (!cursor->x11.handle)
- return GLFW_FALSE;
-
- return GLFW_TRUE;
-}
-
-int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape)
-{
- if (_glfw.x11.xcursor.handle)
- {
- char* theme = XcursorGetTheme(_glfw.x11.display);
- if (theme)
- {
- const int size = XcursorGetDefaultSize(_glfw.x11.display);
- const char* name = NULL;
-
- if (shape == GLFW_ARROW_CURSOR)
- name = "default";
- else if (shape == GLFW_IBEAM_CURSOR)
- name = "text";
- else if (shape == GLFW_CROSSHAIR_CURSOR)
- name = "crosshair";
- else if (shape == GLFW_POINTING_HAND_CURSOR)
- name = "pointer";
- else if (shape == GLFW_RESIZE_EW_CURSOR)
- name = "ew-resize";
- else if (shape == GLFW_RESIZE_NS_CURSOR)
- name = "ns-resize";
- else if (shape == GLFW_RESIZE_NWSE_CURSOR)
- name = "nwse-resize";
- else if (shape == GLFW_RESIZE_NESW_CURSOR)
- name = "nesw-resize";
- else if (shape == GLFW_RESIZE_ALL_CURSOR)
- name = "all-scroll";
- else if (shape == GLFW_NOT_ALLOWED_CURSOR)
- name = "not-allowed";
-
- XcursorImage* image = XcursorLibraryLoadImage(name, theme, size);
- if (image)
- {
- cursor->x11.handle = XcursorImageLoadCursor(_glfw.x11.display, image);
- XcursorImageDestroy(image);
- }
- }
- }
-
- if (!cursor->x11.handle)
- {
- unsigned int native = 0;
-
- if (shape == GLFW_ARROW_CURSOR)
- native = XC_left_ptr;
- else if (shape == GLFW_IBEAM_CURSOR)
- native = XC_xterm;
- else if (shape == GLFW_CROSSHAIR_CURSOR)
- native = XC_crosshair;
- else if (shape == GLFW_POINTING_HAND_CURSOR)
- native = XC_hand2;
- else if (shape == GLFW_RESIZE_EW_CURSOR)
- native = XC_sb_h_double_arrow;
- else if (shape == GLFW_RESIZE_NS_CURSOR)
- native = XC_sb_v_double_arrow;
- else if (shape == GLFW_RESIZE_ALL_CURSOR)
- native = XC_fleur;
- else
- {
- _glfwInputError(GLFW_CURSOR_UNAVAILABLE,
- "X11: Standard cursor shape unavailable");
- return GLFW_FALSE;
- }
-
- cursor->x11.handle = XCreateFontCursor(_glfw.x11.display, native);
- if (!cursor->x11.handle)
- {
- _glfwInputError(GLFW_PLATFORM_ERROR,
- "X11: Failed to create standard cursor");
- return GLFW_FALSE;
- }
- }
-
- return GLFW_TRUE;
-}
-
-void _glfwPlatformDestroyCursor(_GLFWcursor* cursor)
-{
- if (cursor->x11.handle)
- XFreeCursor(_glfw.x11.display, cursor->x11.handle);
-}
-
-void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor)
-{
- if (window->cursorMode == GLFW_CURSOR_NORMAL)
- {
- updateCursorImage(window);
- XFlush(_glfw.x11.display);
- }
-}
-
-void _glfwPlatformSetClipboardString(const char* string)
-{
- char* copy = _glfw_strdup(string);
- free(_glfw.x11.clipboardString);
- _glfw.x11.clipboardString = copy;
-
- XSetSelectionOwner(_glfw.x11.display,
- _glfw.x11.CLIPBOARD,
- _glfw.x11.helperWindowHandle,
- CurrentTime);
-
- if (XGetSelectionOwner(_glfw.x11.display, _glfw.x11.CLIPBOARD) !=
- _glfw.x11.helperWindowHandle)
- {
- _glfwInputError(GLFW_PLATFORM_ERROR,
- "X11: Failed to become owner of clipboard selection");
- }
-}
-
-const char* _glfwPlatformGetClipboardString(void)
-{
- return getSelectionString(_glfw.x11.CLIPBOARD);
-}
-
-EGLenum _glfwPlatformGetEGLPlatform(EGLint** attribs)
-{
- if (_glfw.egl.ANGLE_platform_angle)
- {
- int type = 0;
-
- if (_glfw.egl.ANGLE_platform_angle_opengl)
- {
- if (_glfw.hints.init.angleType == GLFW_ANGLE_PLATFORM_TYPE_OPENGL)
- type = EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE;
- }
-
- if (_glfw.egl.ANGLE_platform_angle_vulkan)
- {
- if (_glfw.hints.init.angleType == GLFW_ANGLE_PLATFORM_TYPE_VULKAN)
- type = EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE;
- }
-
- if (type)
- {
- *attribs = calloc(5, sizeof(EGLint));
- (*attribs)[0] = EGL_PLATFORM_ANGLE_TYPE_ANGLE;
- (*attribs)[1] = type;
- (*attribs)[2] = EGL_PLATFORM_ANGLE_NATIVE_PLATFORM_TYPE_ANGLE;
- (*attribs)[3] = EGL_PLATFORM_X11_EXT;
- (*attribs)[4] = EGL_NONE;
- return EGL_PLATFORM_ANGLE_ANGLE;
- }
- }
-
- if (_glfw.egl.EXT_platform_base && _glfw.egl.EXT_platform_x11)
- return EGL_PLATFORM_X11_EXT;
-
- return 0;
-}
-
-EGLNativeDisplayType _glfwPlatformGetEGLNativeDisplay(void)
-{
- return _glfw.x11.display;
-}
-
-EGLNativeWindowType _glfwPlatformGetEGLNativeWindow(_GLFWwindow* window)
-{
- if (_glfw.egl.platform)
- return &window->x11.handle;
- else
- return (EGLNativeWindowType) window->x11.handle;
-}
-
-void _glfwPlatformGetRequiredInstanceExtensions(char** extensions)
-{
- if (!_glfw.vk.KHR_surface)
- return;
-
- if (!_glfw.vk.KHR_xcb_surface || !_glfw.x11.x11xcb.handle)
- {
- if (!_glfw.vk.KHR_xlib_surface)
- return;
- }
-
- extensions[0] = "VK_KHR_surface";
-
- // NOTE: VK_KHR_xcb_surface is preferred due to some early ICDs exposing but
- // not correctly implementing VK_KHR_xlib_surface
- if (_glfw.vk.KHR_xcb_surface && _glfw.x11.x11xcb.handle)
- extensions[1] = "VK_KHR_xcb_surface";
- else
- extensions[1] = "VK_KHR_xlib_surface";
-}
-
-int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance,
- VkPhysicalDevice device,
- uint32_t queuefamily)
-{
- VisualID visualID = XVisualIDFromVisual(DefaultVisual(_glfw.x11.display,
- _glfw.x11.screen));
-
- if (_glfw.vk.KHR_xcb_surface && _glfw.x11.x11xcb.handle)
- {
- PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR
- vkGetPhysicalDeviceXcbPresentationSupportKHR =
- (PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR)
- vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceXcbPresentationSupportKHR");
- if (!vkGetPhysicalDeviceXcbPresentationSupportKHR)
- {
- _glfwInputError(GLFW_API_UNAVAILABLE,
- "X11: Vulkan instance missing VK_KHR_xcb_surface extension");
- return GLFW_FALSE;
- }
-
- xcb_connection_t* connection = XGetXCBConnection(_glfw.x11.display);
- if (!connection)
- {
- _glfwInputError(GLFW_PLATFORM_ERROR,
- "X11: Failed to retrieve XCB connection");
- return GLFW_FALSE;
- }
-
- return vkGetPhysicalDeviceXcbPresentationSupportKHR(device,
- queuefamily,
- connection,
- visualID);
- }
- else
- {
- PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR
- vkGetPhysicalDeviceXlibPresentationSupportKHR =
- (PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR)
- vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceXlibPresentationSupportKHR");
- if (!vkGetPhysicalDeviceXlibPresentationSupportKHR)
- {
- _glfwInputError(GLFW_API_UNAVAILABLE,
- "X11: Vulkan instance missing VK_KHR_xlib_surface extension");
- return GLFW_FALSE;
- }
-
- return vkGetPhysicalDeviceXlibPresentationSupportKHR(device,
- queuefamily,
- _glfw.x11.display,
- visualID);
- }
-}
-
-VkResult _glfwPlatformCreateWindowSurface(VkInstance instance,
- _GLFWwindow* window,
- const VkAllocationCallbacks* allocator,
- VkSurfaceKHR* surface)
-{
- if (_glfw.vk.KHR_xcb_surface && _glfw.x11.x11xcb.handle)
- {
- VkResult err;
- VkXcbSurfaceCreateInfoKHR sci;
- PFN_vkCreateXcbSurfaceKHR vkCreateXcbSurfaceKHR;
-
- xcb_connection_t* connection = XGetXCBConnection(_glfw.x11.display);
- if (!connection)
- {
- _glfwInputError(GLFW_PLATFORM_ERROR,
- "X11: Failed to retrieve XCB connection");
- return VK_ERROR_EXTENSION_NOT_PRESENT;
- }
-
- vkCreateXcbSurfaceKHR = (PFN_vkCreateXcbSurfaceKHR)
- vkGetInstanceProcAddr(instance, "vkCreateXcbSurfaceKHR");
- if (!vkCreateXcbSurfaceKHR)
- {
- _glfwInputError(GLFW_API_UNAVAILABLE,
- "X11: Vulkan instance missing VK_KHR_xcb_surface extension");
- return VK_ERROR_EXTENSION_NOT_PRESENT;
- }
-
- memset(&sci, 0, sizeof(sci));
- sci.sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR;
- sci.connection = connection;
- sci.window = window->x11.handle;
-
- err = vkCreateXcbSurfaceKHR(instance, &sci, allocator, surface);
- if (err)
- {
- _glfwInputError(GLFW_PLATFORM_ERROR,
- "X11: Failed to create Vulkan XCB surface: %s",
- _glfwGetVulkanResultString(err));
- }
-
- return err;
- }
- else
- {
- VkResult err;
- VkXlibSurfaceCreateInfoKHR sci;
- PFN_vkCreateXlibSurfaceKHR vkCreateXlibSurfaceKHR;
-
- vkCreateXlibSurfaceKHR = (PFN_vkCreateXlibSurfaceKHR)
- vkGetInstanceProcAddr(instance, "vkCreateXlibSurfaceKHR");
- if (!vkCreateXlibSurfaceKHR)
- {
- _glfwInputError(GLFW_API_UNAVAILABLE,
- "X11: Vulkan instance missing VK_KHR_xlib_surface extension");
- return VK_ERROR_EXTENSION_NOT_PRESENT;
- }
-
- memset(&sci, 0, sizeof(sci));
- sci.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR;
- sci.dpy = _glfw.x11.display;
- sci.window = window->x11.handle;
-
- err = vkCreateXlibSurfaceKHR(instance, &sci, allocator, surface);
- if (err)
- {
- _glfwInputError(GLFW_PLATFORM_ERROR,
- "X11: Failed to create Vulkan X11 surface: %s",
- _glfwGetVulkanResultString(err));
- }
-
- return err;
- }
-}
-
-
-//////////////////////////////////////////////////////////////////////////
-////// GLFW native API //////
-//////////////////////////////////////////////////////////////////////////
-
-GLFWAPI Display* glfwGetX11Display(void)
-{
- _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
- return _glfw.x11.display;
-}
-
-GLFWAPI Window glfwGetX11Window(GLFWwindow* handle)
-{
- _GLFWwindow* window = (_GLFWwindow*) handle;
- _GLFW_REQUIRE_INIT_OR_RETURN(None);
- return window->x11.handle;
-}
-
-GLFWAPI void glfwSetX11SelectionString(const char* string)
-{
- _GLFW_REQUIRE_INIT();
-
- free(_glfw.x11.primarySelectionString);
- _glfw.x11.primarySelectionString = _glfw_strdup(string);
-
- XSetSelectionOwner(_glfw.x11.display,
- _glfw.x11.PRIMARY,
- _glfw.x11.helperWindowHandle,
- CurrentTime);
-
- if (XGetSelectionOwner(_glfw.x11.display, _glfw.x11.PRIMARY) !=
- _glfw.x11.helperWindowHandle)
- {
- _glfwInputError(GLFW_PLATFORM_ERROR,
- "X11: Failed to become owner of primary selection");
- }
-}
-
-GLFWAPI const char* glfwGetX11SelectionString(void)
-{
- _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
- return getSelectionString(_glfw.x11.PRIMARY);
-}
-