x11_window.c 98.3 KB
Newer Older
Camilla Berglund's avatar
Camilla Berglund committed
1
//========================================================================
Camilla Berglund's avatar
Camilla Berglund committed
2
// GLFW 3.3 X11 - www.glfw.org
Camilla Berglund's avatar
Camilla Berglund committed
3
4
//------------------------------------------------------------------------
// Copyright (c) 2002-2006 Marcus Geelnard
Camilla Löwy's avatar
Camilla Löwy committed
5
// Copyright (c) 2006-2019 Camilla Löwy <elmindreda@glfw.org>
Camilla Berglund's avatar
Camilla Berglund committed
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//
// 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.
//
//========================================================================
27
28
// It is fine to use C99 in this file because it will not be built with VS
//========================================================================
Camilla Berglund's avatar
Camilla Berglund committed
29
30
31

#include "internal.h"

32
#include <X11/cursorfont.h>
Camilla Berglund's avatar
Camilla Berglund committed
33
#include <X11/Xmd.h>
34

35
36
#include <sys/select.h>

37
38
39
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
40
#include <limits.h>
41
#include <errno.h>
42
#include <assert.h>
Camilla Berglund's avatar
Camilla Berglund committed
43
44
45
46
47
48

// Action for EWMH client messages
#define _NET_WM_STATE_REMOVE        0
#define _NET_WM_STATE_ADD           1
#define _NET_WM_STATE_TOGGLE        2

49
// Additional mouse button names for XButtonEvent
50
51
#define Button6            6
#define Button7            7
52

53
54
55
56
// Motif WM hints flags
#define MWM_HINTS_DECORATIONS   2
#define MWM_DECOR_ALL           1

57
58
#define _GLFW_XDND_VERSION 5

Camilla Berglund's avatar
Camilla Berglund committed
59

Camilla Berglund's avatar
Camilla Berglund committed
60
61
62
// Wait for data to arrive using select
// This avoids blocking other threads via the per-display Xlib lock that also
// covers GLX functions
63
//
64
static GLFWbool waitForEvent(double* timeout)
65
66
67
{
    fd_set fds;
    const int fd = ConnectionNumber(_glfw.x11.display);
68
    int count = fd + 1;
69

Camilla Berglund's avatar
Camilla Berglund committed
70
#if defined(__linux__)
71
    if (_glfw.linjs.inotify > fd)
72
        count = _glfw.linjs.inotify + 1;
73
#endif
74
    for (;;)
75
    {
76
77
78
79
80
81
82
        FD_ZERO(&fds);
        FD_SET(fd, &fds);
#if defined(__linux__)
        if (_glfw.linjs.inotify > 0)
            FD_SET(_glfw.linjs.inotify, &fds);
#endif

83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
        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;
103
    }
104
105
}

106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
// 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;
}

126
127
128
129
130
131
132
133
134
135
// Returns whether the window is iconified
//
static int getWindowState(_GLFWwindow* window)
{
    int result = WithdrawnState;
    struct {
        CARD32 state;
        Window icon;
    } *state = NULL;

136
137
138
139
    if (_glfwGetWindowPropertyX11(window->x11.handle,
                                  _glfw.x11.WM_STATE,
                                  _glfw.x11.WM_STATE,
                                  (unsigned char**) &state) >= 2)
140
141
142
143
    {
        result = state->state;
    }

144
145
146
    if (state)
        XFree(state);

147
148
149
    return result;
}

Camilla Berglund's avatar
Cleanup    
Camilla Berglund committed
150
151
152
153
// Returns whether the event is a selection event
//
static Bool isSelectionEvent(Display* display, XEvent* event, XPointer pointer)
{
154
155
156
    if (event->xany.window != _glfw.x11.helperWindowHandle)
        return False;

Camilla Berglund's avatar
Cleanup    
Camilla Berglund committed
157
158
159
160
161
    return event->type == SelectionRequest ||
           event->type == SelectionNotify ||
           event->type == SelectionClear;
}

Camilla Berglund's avatar
Camilla Berglund committed
162
// Returns whether it is a _NET_FRAME_EXTENTS event for the specified window
163
164
165
166
167
168
169
170
171
172
//
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;
}

173
174
175
176
177
178
179
180
181
182
183
// 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;
}

184
185
// Translates an X event modifier state mask
//
Camilla Berglund's avatar
Camilla Berglund committed
186
static int translateState(int state)
187
188
189
190
191
192
{
    int mods = 0;

    if (state & ShiftMask)
        mods |= GLFW_MOD_SHIFT;
    if (state & ControlMask)
193
        mods |= GLFW_MOD_CONTROL;
194
195
    if (state & Mod1Mask)
        mods |= GLFW_MOD_ALT;
Noel Cower's avatar
Noel Cower committed
196
197
    if (state & Mod4Mask)
        mods |= GLFW_MOD_SUPER;
198
199
200
201
    if (state & LockMask)
        mods |= GLFW_MOD_CAPS_LOCK;
    if (state & Mod2Mask)
        mods |= GLFW_MOD_NUM_LOCK;
202
203
204
205

    return mods;
}

206
// Translates an X11 key code to a GLFW key token
207
//
208
static int translateKey(int scancode)
Camilla Berglund's avatar
Camilla Berglund committed
209
{
210
211
    // Use the pre-filled LUT (see createKeyTables() in x11_init.c)
    if (scancode < 0 || scancode > 255)
Camilla Berglund's avatar
Camilla Berglund committed
212
        return GLFW_KEY_UNKNOWN;
213

Camilla Berglund's avatar
Cleanup    
Camilla Berglund committed
214
    return _glfw.x11.keycodes[scancode];
Camilla Berglund's avatar
Camilla Berglund committed
215
216
}

217
// Sends an EWMH or ICCCM event to the window manager
Camilla Berglund's avatar
Camilla Berglund committed
218
//
219
220
static void sendEventToWM(_GLFWwindow* window, Atom type,
                          long a, long b, long c, long d, long e)
Camilla Berglund's avatar
Camilla Berglund committed
221
{
222
    XEvent event = { ClientMessage };
Camilla Berglund's avatar
Camilla Berglund committed
223
224
    event.xclient.window = window->x11.handle;
    event.xclient.format = 32; // Data is 32-bit longs
225
226
227
228
229
230
    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;
Camilla Berglund's avatar
Camilla Berglund committed
231

Camilla Berglund's avatar
Camilla Berglund committed
232
    XSendEvent(_glfw.x11.display, _glfw.x11.root,
Camilla Berglund's avatar
Camilla Berglund committed
233
234
235
236
237
               False,
               SubstructureNotifyMask | SubstructureRedirectMask,
               &event);
}

Camilla Berglund's avatar
Camilla Berglund committed
238
239
// Updates the normal hints according to the window settings
//
240
static void updateNormalHints(_GLFWwindow* window, int width, int height)
Camilla Berglund's avatar
Camilla Berglund committed
241
242
243
244
245
246
247
{
    XSizeHints* hints = XAllocSizeHints();

    if (!window->monitor)
    {
        if (window->resizable)
        {
248
249
250
            if (window->minwidth != GLFW_DONT_CARE &&
                window->minheight != GLFW_DONT_CARE)
            {
Camilla Berglund's avatar
Cleanup    
Camilla Berglund committed
251
                hints->flags |= PMinSize;
252
253
254
255
256
257
258
                hints->min_width = window->minwidth;
                hints->min_height = window->minheight;
            }

            if (window->maxwidth != GLFW_DONT_CARE &&
                window->maxheight != GLFW_DONT_CARE)
            {
Camilla Berglund's avatar
Cleanup    
Camilla Berglund committed
259
                hints->flags |= PMaxSize;
260
261
262
263
                hints->max_width = window->maxwidth;
                hints->max_height = window->maxheight;
            }

Camilla Berglund's avatar
Camilla Berglund committed
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
            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;
        }
    }

280
281
282
    hints->flags |= PWinGravity;
    hints->win_gravity = StaticGravity;

Camilla Berglund's avatar
Camilla Berglund committed
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
    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;
        }

333
        // Enable compositor bypass
334
        if (!window->x11.transparent)
Camilla Berglund's avatar
Camilla Berglund committed
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
        {
            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;
        }

372
        // Disable compositor bypass
373
        if (!window->x11.transparent)
Camilla Berglund's avatar
Camilla Berglund committed
374
375
376
377
378
379
380
        {
            XDeleteProperty(_glfw.x11.display, window->x11.handle,
                            _glfw.x11.NET_WM_BYPASS_COMPOSITOR);
        }
    }
}

Camilla Berglund's avatar
Camilla Berglund committed
381
// Splits and translates a text/uri-list into separate file paths
382
// NOTE: This function destroys the provided string
383
//
Camilla Berglund's avatar
Camilla Berglund committed
384
static char** parseUriList(char* text, int* count)
385
386
{
    const char* prefix = "file://";
387
    char** paths = NULL;
388
389
390
391
392
393
394
395
    char* line;

    *count = 0;

    while ((line = strtok(text, "\r\n")))
    {
        text = NULL;

396
        if (line[0] == '#')
397
398
399
            continue;

        if (strncmp(line, prefix, strlen(prefix)) == 0)
400
        {
401
            line += strlen(prefix);
402
403
404
405
            // TODO: Validate hostname
            while (*line != '/')
                line++;
        }
406
407
408

        (*count)++;

409
410
411
        char* path = calloc(strlen(line) + 1, 1);
        paths = realloc(paths, *count * sizeof(char*));
        paths[*count - 1] = path;
412
413
414
415
416
417

        while (*line)
        {
            if (line[0] == '%' && line[1] && line[2])
            {
                const char digits[3] = { line[1], line[2], '\0' };
418
                *path = strtol(digits, NULL, 16);
419
420
421
                line += 2;
            }
            else
422
                *path = *line;
423

424
            path++;
425
426
427
428
            line++;
        }
    }

429
    return paths;
430
431
}

432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
// 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)
//
#if defined(X_HAVE_UTF8_STRING)
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];
}
#endif /*X_HAVE_UTF8_STRING*/

// 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;
}

507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
// 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
Camilla Berglund's avatar
Cleanup    
Camilla Berglund committed
522
523
524
525
    {
        XDefineCursor(_glfw.x11.display, window->x11.handle,
                      _glfw.x11.hiddenCursorHandle);
    }
526
527
}

528
// Enable XI2 raw mouse motion events
529
//
530
static void enableRawMouseMotion(_GLFWwindow* window)
531
{
532
533
    XIEventMask em;
    unsigned char mask[XIMaskLen(XI_RawMotion)] = { 0 };
534

535
536
537
538
    em.deviceid = XIAllMasterDevices;
    em.mask_len = sizeof(mask);
    em.mask = mask;
    XISetMask(mask, XI_RawMotion);
539

540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
    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);
563
564
565
566
567
568

    _glfw.x11.disabledCursorWindow = window;
    _glfwPlatformGetCursorPos(window,
                              &_glfw.x11.restoreCursorPosX,
                              &_glfw.x11.restoreCursorPosY);
    updateCursorImage(window);
Camilla Löwy's avatar
Cleanup    
Camilla Löwy committed
569
    _glfwCenterCursorInContentArea(window);
570
571
572
573
574
575
576
577
578
579
580
581
    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)
{
582
583
    if (window->rawMouseMotion)
        disableRawMouseMotion(window);
584
585
586
587
588
589
590
591
592

    _glfw.x11.disabledCursorWindow = NULL;
    XUngrabPointer(_glfw.x11.display, CurrentTime);
    _glfwPlatformSetCursorPos(window,
                              _glfw.x11.restoreCursorPosX,
                              _glfw.x11.restoreCursorPosY);
    updateCursorImage(window);
}

Camilla Berglund's avatar
Camilla Berglund committed
593
// Create the X11 window (and its colormap)
594
//
Camilla Berglund's avatar
Cleanup    
Camilla Berglund committed
595
596
597
static GLFWbool createNativeWindow(_GLFWwindow* window,
                                   const _GLFWwndconfig* wndconfig,
                                   Visual* visual, int depth)
Camilla Berglund's avatar
Camilla Berglund committed
598
{
Camilla Löwy's avatar
Camilla Löwy committed
599
600
601
602
603
604
605
606
607
    int width = wndconfig->width;
    int height = wndconfig->height;

    if (wndconfig->scaleToMonitor)
    {
        width *= _glfw.x11.contentScaleX;
        height *= _glfw.x11.contentScaleY;
    }

Camilla Berglund's avatar
Camilla Berglund committed
608
    // Create a colormap based on the visual used by the current context
609
610
    window->x11.colormap = XCreateColormap(_glfw.x11.display,
                                           _glfw.x11.root,
611
                                           visual,
612
                                           AllocNone);
Camilla Berglund's avatar
Camilla Berglund committed
613

614
615
    window->x11.transparent = _glfwIsVisualTransparentX11(visual);

Camilla Löwy's avatar
Camilla Löwy committed
616
617
618
619
620
621
622
623
624
    XSetWindowAttributes wa = { 0 };
    wa.colormap = window->x11.colormap;
    wa.event_mask = StructureNotifyMask | KeyPressMask | KeyReleaseMask |
                    PointerMotionMask | ButtonPressMask | ButtonReleaseMask |
                    ExposureMask | FocusChangeMask | VisibilityChangeMask |
                    EnterWindowMask | LeaveWindowMask | PropertyChangeMask;

    _glfwGrabErrorHandlerX11();

625
    window->x11.parent = _glfw.x11.root;
Camilla Löwy's avatar
Camilla Löwy committed
626
627
628
629
630
631
632
633
634
635
636
637
638
639
    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)
Camilla Berglund's avatar
Camilla Berglund committed
640
    {
Camilla Löwy's avatar
Camilla Löwy committed
641
642
643
        _glfwInputErrorX11(GLFW_PLATFORM_ERROR,
                           "X11: Failed to create window");
        return GLFW_FALSE;
Camilla Berglund's avatar
Camilla Berglund committed
644
645
    }

Camilla Löwy's avatar
Camilla Löwy committed
646
647
648
649
650
    XSaveContext(_glfw.x11.display,
                 window->x11.handle,
                 _glfw.x11.context,
                 (XPointer) window);

Camilla Berglund's avatar
Camilla Berglund committed
651
    if (!wndconfig->decorated)
Camilla Löwy's avatar
Camilla Löwy committed
652
        _glfwPlatformSetWindowDecorated(window, GLFW_FALSE);
Camilla Berglund's avatar
Camilla Berglund committed
653

654
    if (_glfw.x11.NET_WM_STATE && !window->monitor)
Camilla Berglund's avatar
Camilla Berglund committed
655
    {
656
657
658
659
        Atom states[3];
        int count = 0;

        if (wndconfig->floating)
660
        {
661
662
            if (_glfw.x11.NET_WM_STATE_ABOVE)
                states[count++] = _glfw.x11.NET_WM_STATE_ABOVE;
Camilla Berglund's avatar
Camilla Berglund committed
663
        }
664

665
        if (wndconfig->maximized)
666
        {
667
668
            if (_glfw.x11.NET_WM_STATE_MAXIMIZED_VERT &&
                _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ)
669
            {
670
671
                states[count++] = _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT;
                states[count++] = _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ;
672
                window->x11.maximized = GLFW_TRUE;
673
674
            }
        }
675

676
677
        if (count)
        {
Camilla Berglund's avatar
Camilla Berglund committed
678
679
            XChangeProperty(_glfw.x11.display, window->x11.handle,
                            _glfw.x11.NET_WM_STATE, XA_ATOM, 32,
Camilla Löwy's avatar
Camilla Löwy committed
680
                            PropModeReplace, (unsigned char*) states, count);
681
        }
Camilla Berglund's avatar
Camilla Berglund committed
682
683
    }

684
    // Declare the WM protocols supported by GLFW
Camilla Berglund's avatar
Camilla Berglund committed
685
    {
686
        Atom protocols[] =
Camilla Berglund's avatar
Camilla Berglund committed
687
        {
688
689
690
691
692
693
            _glfw.x11.WM_DELETE_WINDOW,
            _glfw.x11.NET_WM_PING
        };

        XSetWMProtocols(_glfw.x11.display, window->x11.handle,
                        protocols, sizeof(protocols) / sizeof(Atom));
Camilla Berglund's avatar
Camilla Berglund committed
694
695
    }

696
    // Declare our PID
697
    {
698
        const long pid = getpid();
699
700
701
702
703
704
705

        XChangeProperty(_glfw.x11.display,  window->x11.handle,
                        _glfw.x11.NET_WM_PID, XA_CARDINAL, 32,
                        PropModeReplace,
                        (unsigned char*) &pid, 1);
    }

706
707
708
709
710
711
712
713
    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);
    }

Camilla Berglund's avatar
Camilla Berglund committed
714
715
    // Set ICCCM WM_HINTS property
    {
716
        XWMHints* hints = XAllocWMHints();
717
        if (!hints)
718
        {
719
720
            _glfwInputError(GLFW_OUT_OF_MEMORY,
                            "X11: Failed to allocate WM hints");
721
            return GLFW_FALSE;
722
        }
Camilla Berglund's avatar
Camilla Berglund committed
723
724
725
726

        hints->flags = StateHint;
        hints->initial_state = NormalState;

727
        XSetWMHints(_glfw.x11.display, window->x11.handle, hints);
728
        XFree(hints);
Camilla Berglund's avatar
Camilla Berglund committed
729
730
    }

Camilla Löwy's avatar
Camilla Löwy committed
731
    updateNormalHints(window, width, height);
Camilla Berglund's avatar
Camilla Berglund committed
732

733
734
735
    // Set ICCCM WM_CLASS property
    {
        XClassHint* hint = XAllocClassHint();
Camilla Löwy's avatar
Camilla Löwy committed
736

737
738
        if (strlen(wndconfig->x11.instanceName) &&
            strlen(wndconfig->x11.className))
Camilla Löwy's avatar
Camilla Löwy committed
739
        {
740
741
            hint->res_name = (char*) wndconfig->x11.instanceName;
            hint->res_class = (char*) wndconfig->x11.className;
Camilla Löwy's avatar
Camilla Löwy committed
742
743
744
        }
        else
        {
745
746
747
748
749
750
751
752
753
754
755
756
            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";
Camilla Löwy's avatar
Camilla Löwy committed
757
        }
758
759
760
761
762

        XSetClassHint(_glfw.x11.display, window->x11.handle, hint);
        XFree(hint);
    }

763
    // Announce support for Xdnd (drag and drop)
arturo's avatar
arturo committed
764
    {
765
        const Atom version = _GLFW_XDND_VERSION;
Camilla Berglund's avatar
Camilla Berglund committed
766
767
768
        XChangeProperty(_glfw.x11.display, window->x11.handle,
                        _glfw.x11.XdndAware, XA_ATOM, 32,
                        PropModeReplace, (unsigned char*) &version, 1);
arturo's avatar
arturo committed
769
770
    }

771
    _glfwPlatformSetWindowTitle(window, wndconfig->title);
Camilla Berglund's avatar
Camilla Berglund committed
772

773
774
775
776
777
778
779
780
781
782
783
    if (_glfw.x11.im)
    {
        window->x11.ic = XCreateIC(_glfw.x11.im,
                                   XNInputStyle,
                                   XIMPreeditNothing | XIMStatusNothing,
                                   XNClientWindow,
                                   window->x11.handle,
                                   XNFocusWindow,
                                   window->x11.handle,
                                   NULL);
    }
Camilla Berglund's avatar
Camilla Berglund committed
784

785
786
787
788
789
790
791
    if (window->x11.ic)
    {
        unsigned long filter = 0;
        if (XGetICValues(window->x11.ic, XNFilterEvents, &filter, NULL) == NULL)
            XSelectInput(_glfw.x11.display, window->x11.handle, wa.event_mask | filter);
    }

792
793
794
    _glfwPlatformGetWindowPos(window, &window->x11.xpos, &window->x11.ypos);
    _glfwPlatformGetWindowSize(window, &window->x11.width, &window->x11.height);

795
    return GLFW_TRUE;
Camilla Berglund's avatar
Camilla Berglund committed
796
797
}

798
799
800
801
802
// Set the specified property to the selection converted to the requested target
//
static Atom writeTargetToProperty(const XSelectionRequestEvent* request)
{
    int i;
Camilla Löwy's avatar
Cleanup    
Camilla Löwy committed
803
    char* selectionString = NULL;
804
    const Atom formats[] = { _glfw.x11.UTF8_STRING, XA_STRING };
805
    const int formatCount = sizeof(formats) / sizeof(formats[0]);
Camilla Löwy's avatar
Cleanup    
Camilla Löwy committed
806
807
808
809
810

    if (request->selection == _glfw.x11.PRIMARY)
        selectionString = _glfw.x11.primarySelectionString;
    else
        selectionString = _glfw.x11.clipboardString;
811
812
813

    if (request->property == None)
    {
814
        // The requester is a legacy client (ICCCM section 2.2)
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
        // 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;

847
848
849
850
        count = _glfwGetWindowPropertyX11(request->requestor,
                                          request->property,
                                          _glfw.x11.ATOM_PAIR,
                                          (unsigned char**) &targets);
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869

        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,
870
871
                                (unsigned char *) selectionString,
                                strlen(selectionString));
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
            }
            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,
Camilla Berglund's avatar
Camilla Berglund committed
899
                        _glfw.x11.NULL_,
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
                        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,
922
923
                            (unsigned char *) selectionString,
                            strlen(selectionString));
924
925
926
927
928
929
930
931
932
933
934
935

            return request->property;
        }
    }

    // The requested target is not supported

    return None;
}

static void handleSelectionClear(XEvent* event)
{
Camilla Löwy's avatar
Cleanup    
Camilla Löwy committed
936
    if (event->xselectionclear.selection == _glfw.x11.PRIMARY)
937
938
939
940
    {
        free(_glfw.x11.primarySelectionString);
        _glfw.x11.primarySelectionString = NULL;
    }
Camilla Löwy's avatar
Cleanup    
Camilla Löwy committed
941
    else
942
943
944
945
    {
        free(_glfw.x11.clipboardString);
        _glfw.x11.clipboardString = NULL;
    }
946
947
948
949
950
951
}

static void handleSelectionRequest(XEvent* event)
{
    const XSelectionRequestEvent* request = &event->xselectionrequest;

952
    XEvent reply = { SelectionNotify };
Camilla Berglund's avatar
Camilla Berglund committed
953
954
955
956
957
958
959
960
    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);
961
962
}

Camilla Löwy's avatar
Cleanup    
Camilla Löwy committed
963
static const char* getSelectionString(Atom selection)
964
{
Camilla Löwy's avatar
Cleanup    
Camilla Löwy committed
965
    char** selectionString = NULL;
966
    const Atom targets[] = { _glfw.x11.UTF8_STRING, XA_STRING };
967
    const size_t targetCount = sizeof(targets) / sizeof(targets[0]);
968

Camilla Löwy's avatar
Cleanup    
Camilla Löwy committed
969
970
971
972
973
    if (selection == _glfw.x11.PRIMARY)
        selectionString = &_glfw.x11.primarySelectionString;
    else
        selectionString = &_glfw.x11.clipboardString;

974
975
976
977
978
    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
Camilla Löwy's avatar
Cleanup    
Camilla Löwy committed
979
        return *selectionString;
980
981
    }

Camilla Löwy's avatar
Cleanup    
Camilla Löwy committed
982
983
    free(*selectionString);
    *selectionString = NULL;
984

985
    for (size_t i = 0;  i < targetCount;  i++)
986
987
    {
        char* data;
988
989
990
991
        Atom actualType;
        int actualFormat;
        unsigned long itemCount, bytesAfter;
        XEvent notification, dummy;
992
993
994

        XConvertSelection(_glfw.x11.display,
                          selection,
995
                          targets[i],
996
997
998
999
1000
1001
1002
                          _glfw.x11.GLFW_SELECTION,
                          _glfw.x11.helperWindowHandle,
                          CurrentTime);

        while (!XCheckTypedWindowEvent(_glfw.x11.display,
                                       _glfw.x11.helperWindowHandle,
                                       SelectionNotify,
1003
                                       &notification))
1004
1005
1006
1007
        {
            waitForEvent(NULL);
        }

1008
        if (notification.xselection.property == None)
1009
1010
            continue;

1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
        XCheckIfEvent(_glfw.x11.display,
                      &dummy,
                      isSelPropNewValueNotify,
                      (XPointer) &notification);

        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)
1030
        {
1031
            size_t size = 1;
1032
            char* string = NULL;
1033

1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
            for (;;)
            {
                while (!XCheckIfEvent(_glfw.x11.display,
                                      &dummy,
                                      isSelPropNewValueNotify,
                                      (XPointer) &notification))
                {
                    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;
1061
1062
1063
                    string = realloc(string, size);
                    string[size - itemCount - 1] = '\0';
                    strcat(string, data);
1064
1065
1066
                }

                if (!itemCount)
1067
1068
1069
1070
1071
1072
1073
1074
1075
                {
                    if (targets[i] == XA_STRING)
                    {
                        *selectionString = convertLatin1toUTF8(string);
                        free(string);
                    }
                    else
                        *selectionString = string;

1076
                    break;
1077
                }
1078
1079
1080
            }
        }
        else if (actualType == targets[i])
1081
1082
1083
1084
        {
            if (targets[i] == XA_STRING)
                *selectionString = convertLatin1toUTF8(data);
            else
1085
                *selectionString = _glfw_strdup(data);
1086
        }
1087

1088
        XFree(data);
1089

Camilla Löwy's avatar
Cleanup    
Camilla Löwy committed
1090
        if (*selectionString)
1091
1092
1093
            break;
    }

Camilla Löwy's avatar
Cleanup    
Camilla Löwy committed
1094
    if (!*selectionString)
1095
1096
    {
        _glfwInputError(GLFW_FORMAT_UNAVAILABLE,
1097
                        "X11: Failed to convert selection to string");
1098
1099
    }

Camilla Löwy's avatar
Cleanup    
Camilla Löwy committed
1100
    return *selectionString;
1101
1102
}

1103
// Make the specified window and its video mode active on its monitor
1104
//
Camilla Löwy's avatar
Cleanup    
Camilla Löwy committed
1105
static void acquireMonitor(_GLFWwindow* window)
Camilla Berglund's avatar
Camilla Berglund committed
1106
{
1107
    if (_glfw.x11.saver.count == 0)
Camilla Berglund's avatar
Camilla Berglund committed
1108
1109
    {
        // Remember old screen saver settings
1110
1111
1112
1113
1114
        XGetScreenSaver(_glfw.x11.display,
                        &_glfw.x11.saver.timeout,
                        &_glfw.x11.saver.interval,
                        &_glfw.x11.saver.blanking,
                        &_glfw.x11.saver.exposure);
Camilla Berglund's avatar
Camilla Berglund committed
1115
1116

        // Disable screen saver
1117
        XSetScreenSaver(_glfw.x11.display, 0, 0, DontPreferBlanking,
1118
                        DefaultExposures);
Camilla Berglund's avatar
Camilla Berglund committed
1119
1120
    }

Camilla Berglund's avatar
Camilla Berglund committed
1121
1122
    if (!window->monitor->window)
        _glfw.x11.saver.count++;
1123

Camilla Löwy's avatar
Cleanup    
Camilla Löwy committed
1124
    _glfwSetVideoModeX11(window->monitor, &window->videoMode);
Camilla Berglund's avatar
Camilla Berglund committed
1125

Camilla Berglund's avatar
Camilla Berglund committed
1126
    if (window->x11.overrideRedirect)
1127
1128
1129
1130
    {
        int xpos, ypos;
        GLFWvidmode mode;

Camilla Berglund's avatar
Camilla Berglund committed
1131
        // Manually position the window over its monitor
1132
1133
1134
1135
1136
1137
1138
        _glfwPlatformGetMonitorPos(window->monitor, &xpos, &ypos);
        _glfwPlatformGetVideoMode(window->monitor, &mode);

        XMoveResizeWindow(_glfw.x11.display, window->x11.handle,
                          xpos, ypos, mode.width, mode.height);
    }

Camilla Löwy's avatar
Cleanup    
Camilla Löwy committed
1139
    _glfwInputMonitorWindow(window->monitor, window);
Camilla Berglund's avatar
Camilla Berglund committed
1140
1141
}

1142
// Remove the window and restore the original video mode
1143
//
1144
static void releaseMonitor(_GLFWwindow* window)
Camilla Berglund's avatar
Camilla Berglund committed
1145
{
1146
1147
1148
    if (window->monitor->window != window)
        return;

Camilla Löwy's avatar
Cleanup    
Camilla Löwy committed
1149
    _glfwInputMonitorWindow(window->monitor, NULL);
1150
    _glfwRestoreVideoModeX11(window->monitor);
Camilla Berglund's avatar
Camilla Berglund committed
1151

1152
1153
1154
    _glfw.x11.saver.count--;

    if (_glfw.x11.saver.count == 0)
Camilla Berglund's avatar
Camilla Berglund committed
1155
1156
    {
        // Restore old screen saver settings
1157
1158
1159
1160
1161
        XSetScreenSaver(_glfw.x11.display,
                        _glfw.x11.saver.timeout,
                        _glfw.x11.saver.interval,
                        _glfw.x11.saver.blanking,
                        _glfw.x11.saver.exposure);
Camilla Berglund's avatar
Camilla Berglund committed
1162
1163
1164
    }
}

Camilla Berglund's avatar
Camilla Berglund committed
1165
// Process the specified X event
1166
//
Camilla Berglund's avatar
Camilla Berglund committed
1167
static void processEvent(XEvent *event)
Camilla Berglund's avatar
Camilla Berglund committed
1168
{
1169
    int keycode = 0;
1170
1171
    Bool filtered = False;

1172
1173
1174
1175
    // HACK: Save scancode as some IMs clear the field in XFilterEvent
    if (event->type == KeyPress || event->type == KeyRelease)
        keycode = event->xkey.keycode;

1176
1177
    if (_glfw.x11.im)
        filtered = XFilterEvent(event, None);
1178

1179
1180
1181
1182
1183
    if (_glfw.x11.randr.available)
    {
        if (event->type == _glfw.x11.randr.eventBase + RRNotify)
        {
            XRRUpdateConfiguration(event);
1184
            _glfwPollMonitorsX11();
1185
1186
1187
1188
            return;
        }
    }

Camilla Löwy's avatar
Cleanup    
Camilla Löwy committed
1189
    if (_glfw.x11.xkb.available)
1190
    {
Camilla Löwy's avatar
Cleanup    
Camilla Löwy committed
1191
        if (event->type == _glfw.x11.xkb.eventBase + XkbEventCode)
1192
        {
Camilla Löwy's avatar
Cleanup    
Camilla Löwy committed
1193
1194
1195
1196
1197
            if (((XkbEvent*) event)->any.xkb_type == XkbStateNotify &&
                (((XkbEvent*) event)->state.changed & XkbGroupStateMask))
            {
                _glfw.x11.xkb.group = ((XkbEvent*) event)->state.group;
            }
1198
1199
1200
        }
    }

1201
    if (event->type == GenericEvent)
1202
    {
1203
        if (_glfw.x11.xi.available)
1204
        {
1205
1206
1207
            _GLFWwindow* window = _glfw.x11.disabledCursorWindow;

            if (window &&
1208
                window->rawMouseMotion &&
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
                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);
1234
        }
1235
1236
1237
1238

        return;
    }

1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
    if (event->type == SelectionClear)
    {
        handleSelectionClear(event);
        return;
    }
    else if (event->type == SelectionRequest)
    {
        handleSelectionRequest(event);
        return;
    }

1250
    _GLFWwindow* window = NULL;
Camilla Löwy's avatar
Camilla Löwy committed
1251
1252
1253
1254
    if (XFindContext(_glfw.x11.display,
                     event->xany.window,
                     _glfw.x11.context,
                     (XPointer*) &window) != 0)
1255
1256
1257
    {
        // This is an event for a window that has already been destroyed
        return;
1258
    }
1259

1260
    switch (event->type)
Camilla Berglund's avatar
Camilla Berglund committed
1261
    {
1262
1263
1264
1265
1266
1267
        case ReparentNotify:
        {
            window->x11.parent = event->xreparent.parent;
            return;
        }

Camilla Berglund's avatar
Camilla Berglund committed
1268
1269
        case KeyPress:
        {
1270
            const int key = translateKey(keycode);
1271
            const int mods = translateState(event->xkey.state);
1272
            const int plain = !(mods & (GLFW_MOD_CONTROL | GLFW_MOD_ALT));
1273

1274
            if (window->x11.ic)
1275
            {
1276
                // HACK: Ignore duplicate key press events generated by ibus
1277
1278
1279
1280
                //       These have the same timestamp as the original event
                //       Corresponding release events are filtered out
                //       implicitly by the GLFW key repeat logic
                if (window->x11.lastKeyTime < event->xkey.time)
1281
                {
1282
1283
1284
                    if (keycode)
                        _glfwInputKey(window, key, keycode, GLFW_PRESS, mods);

1285
1286
                    window->x11.lastKeyTime = event->xkey.time;
                }
1287
1288

                if (!filtered)
1289
                {
1290
                    int count;
1291
                    Status status;
1292
#if defined(X_HAVE_UTF8_STRING)
1293
                    char buffer[100];
1294
                    char* chars = buffer;
1295