wl_window.c 46.5 KB
Newer Older
1
//========================================================================
Camilla Berglund's avatar
Camilla Berglund committed
2
// GLFW 3.3 Wayland - www.glfw.org
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//------------------------------------------------------------------------
// Copyright (c) 2014 Jonas Ådahl <jadahl@gmail.com>
//
// 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
#define _GNU_SOURCE

29
30
31
#include "internal.h"

#include <stdio.h>
32
33
34
35
36
37
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <sys/mman.h>
38
#include <sys/timerfd.h>
39
#include <poll.h>
40

41

Camilla Berglund's avatar
Camilla Berglund committed
42
43
44
static void handlePing(void* data,
                       struct wl_shell_surface* shellSurface,
                       uint32_t serial)
45
46
47
48
{
    wl_shell_surface_pong(shellSurface, serial);
}

Camilla Berglund's avatar
Camilla Berglund committed
49
50
51
52
53
static void handleConfigure(void* data,
                            struct wl_shell_surface* shellSurface,
                            uint32_t edges,
                            int32_t width,
                            int32_t height)
54
{
55
    _GLFWwindow* window = data;
56
57
58
59
60
    float aspectRatio;
    float targetRatio;

    if (!window->monitor)
    {
61
        if (_glfw.wl.viewporter && window->decorated)
62
        {
63
64
            width -= _GLFW_DECORATION_HORIZONTAL;
            height -= _GLFW_DECORATION_VERTICAL;
65
        }
66
67
68
69
        if (width < 1)
            width = 1;
        if (height < 1)
            height = 1;
70

71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
        if (window->numer != GLFW_DONT_CARE && window->denom != GLFW_DONT_CARE)
        {
            aspectRatio = (float)width / (float)height;
            targetRatio = (float)window->numer / (float)window->denom;
            if (aspectRatio < targetRatio)
                height = width / targetRatio;
            else if (aspectRatio > targetRatio)
                width = height * targetRatio;
        }

        if (window->minwidth != GLFW_DONT_CARE && width < window->minwidth)
            width = window->minwidth;
        else if (window->maxwidth != GLFW_DONT_CARE && width > window->maxwidth)
            width = window->maxwidth;

        if (window->minheight != GLFW_DONT_CARE && height < window->minheight)
            height = window->minheight;
        else if (window->maxheight != GLFW_DONT_CARE && height > window->maxheight)
            height = window->maxheight;
    }

92
93
94
    _glfwInputWindowSize(window, width, height);
    _glfwPlatformSetWindowSize(window, width, height);
    _glfwInputWindowDamage(window);
95
96
}

Camilla Berglund's avatar
Camilla Berglund committed
97
98
static void handlePopupDone(void* data,
                            struct wl_shell_surface* shellSurface)
99
100
101
102
103
104
105
106
107
{
}

static const struct wl_shell_surface_listener shellSurfaceListener = {
    handlePing,
    handleConfigure,
    handlePopupDone
};

108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
static int
createTmpfileCloexec(char* tmpname)
{
    int fd;

    fd = mkostemp(tmpname, O_CLOEXEC);
    if (fd >= 0)
        unlink(tmpname);

    return fd;
}

/*
 * Create a new, unique, anonymous file of the given size, and
 * return the file descriptor for it. The file descriptor is set
 * CLOEXEC. The file is immediately suitable for mmap()'ing
 * the given size at offset zero.
 *
 * The file should not have a permanent backing store like a disk,
 * but may have if XDG_RUNTIME_DIR is not properly implemented in OS.
 *
 * The file name is deleted from the file system.
 *
 * The file is suitable for buffer sharing between processes by
 * transmitting the file descriptor over Unix sockets using the
 * SCM_RIGHTS methods.
 *
 * posix_fallocate() is used to guarantee that disk space is available
 * for the file at the given size. If disk space is insufficent, errno
 * is set to ENOSPC. If posix_fallocate() is not supported, program may
 * receive SIGBUS on accessing mmap()'ed file contents instead.
 */
static int
createAnonymousFile(off_t size)
{
    static const char template[] = "/glfw-shared-XXXXXX";
    const char* path;
    char* name;
    int fd;
    int ret;

    path = getenv("XDG_RUNTIME_DIR");
    if (!path)
    {
        errno = ENOENT;
        return -1;
    }

    name = calloc(strlen(path) + sizeof(template), 1);
    strcpy(name, path);
    strcat(name, template);

    fd = createTmpfileCloexec(name);

    free(name);

    if (fd < 0)
        return -1;
    ret = posix_fallocate(fd, 0, size);
    if (ret != 0)
    {
        close(fd);
        errno = ret;
        return -1;
    }
    return fd;
}

static struct wl_buffer* createShmBuffer(const GLFWimage* image)
{
    struct wl_shm_pool* pool;
    struct wl_buffer* buffer;
    int stride = image->width * 4;
    int length = image->width * image->height * 4;
    void* data;
    int fd, i;

    fd = createAnonymousFile(length);
    if (fd < 0)
    {
        _glfwInputError(GLFW_PLATFORM_ERROR,
                        "Wayland: Creating a buffer file for %d B failed: %m",
                        length);
        return GLFW_FALSE;
    }

    data = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (data == MAP_FAILED)
    {
        _glfwInputError(GLFW_PLATFORM_ERROR,
                        "Wayland: mmap failed: %m");
        close(fd);
        return GLFW_FALSE;
    }

    pool = wl_shm_create_pool(_glfw.wl.shm, fd, length);

    close(fd);
    unsigned char* source = (unsigned char*) image->pixels;
    unsigned char* target = data;
    for (i = 0;  i < image->width * image->height;  i++, source += 4)
    {
        unsigned int alpha = source[3];

        *target++ = (unsigned char) ((source[2] * alpha) / 255);
        *target++ = (unsigned char) ((source[1] * alpha) / 255);
        *target++ = (unsigned char) ((source[0] * alpha) / 255);
        *target++ = (unsigned char) alpha;
    }

    buffer =
        wl_shm_pool_create_buffer(pool, 0,
                                  image->width,
                                  image->height,
                                  stride, WL_SHM_FORMAT_ARGB8888);
    munmap(data, length);
    wl_shm_pool_destroy(pool);

    return buffer;
}

static void createDecoration(_GLFWdecorationWayland* decoration,
                             struct wl_surface* parent,
231
232
                             struct wl_buffer* buffer, GLFWbool opaque,
                             int x, int y,
233
234
                             int width, int height)
{
235
236
    struct wl_region* region;

237
238
239
240
241
242
243
244
245
    decoration->surface = wl_compositor_create_surface(_glfw.wl.compositor);
    decoration->subsurface =
        wl_subcompositor_get_subsurface(_glfw.wl.subcompositor,
                                        decoration->surface, parent);
    wl_subsurface_set_position(decoration->subsurface, x, y);
    decoration->viewport = wp_viewporter_get_viewport(_glfw.wl.viewporter,
                                                      decoration->surface);
    wp_viewport_set_destination(decoration->viewport, width, height);
    wl_surface_attach(decoration->surface, buffer, 0, 0);
246
247
248
249
250
251
252
253
254
255
256

    if (opaque)
    {
        region = wl_compositor_create_region(_glfw.wl.compositor);
        wl_region_add(region, 0, 0, width, height);
        wl_surface_set_opaque_region(decoration->surface, region);
        wl_surface_commit(decoration->surface);
        wl_region_destroy(region);
    }
    else
        wl_surface_commit(decoration->surface);
257
258
259
260
261
262
}

static void createDecorations(_GLFWwindow* window)
{
    unsigned char data[] = { 224, 224, 224, 255 };
    const GLFWimage image = { 1, 1, data };
263
    GLFWbool opaque = (data[3] == 255);
264

265
266
267
    if (!_glfw.wl.viewporter)
        return;

268
269
    if (!window->wl.decorations.buffer)
        window->wl.decorations.buffer = createShmBuffer(&image);
270

271
    createDecoration(&window->wl.decorations.top, window->wl.surface,
272
                     window->wl.decorations.buffer, opaque,
273
274
                     0, -_GLFW_DECORATION_TOP,
                     window->wl.width, _GLFW_DECORATION_TOP);
275
    createDecoration(&window->wl.decorations.left, window->wl.surface,
276
                     window->wl.decorations.buffer, opaque,
277
278
                     -_GLFW_DECORATION_WIDTH, -_GLFW_DECORATION_TOP,
                     _GLFW_DECORATION_WIDTH, window->wl.height + _GLFW_DECORATION_TOP);
279
    createDecoration(&window->wl.decorations.right, window->wl.surface,
280
                     window->wl.decorations.buffer, opaque,
281
282
                     window->wl.width, -_GLFW_DECORATION_TOP,
                     _GLFW_DECORATION_WIDTH, window->wl.height + _GLFW_DECORATION_TOP);
283
    createDecoration(&window->wl.decorations.bottom, window->wl.surface,
284
                     window->wl.decorations.buffer, opaque,
285
                     -_GLFW_DECORATION_WIDTH, window->wl.height,
286
                     window->wl.width + _GLFW_DECORATION_HORIZONTAL, _GLFW_DECORATION_WIDTH);
287
288
}

289
290
291
292
293
294
295
296
static void destroyDecoration(_GLFWdecorationWayland* decoration)
{
    if (decoration->surface)
        wl_surface_destroy(decoration->surface);
    if (decoration->subsurface)
        wl_subsurface_destroy(decoration->subsurface);
    if (decoration->viewport)
        wp_viewport_destroy(decoration->viewport);
297
298
299
    decoration->surface = NULL;
    decoration->subsurface = NULL;
    decoration->viewport = NULL;
300
301
302
303
304
305
306
307
308
309
}

static void destroyDecorations(_GLFWwindow* window)
{
    destroyDecoration(&window->wl.decorations.top);
    destroyDecoration(&window->wl.decorations.left);
    destroyDecoration(&window->wl.decorations.right);
    destroyDecoration(&window->wl.decorations.bottom);
}

310
311
// Makes the surface considered as XRGB instead of ARGB.
static void setOpaqueRegion(_GLFWwindow* window)
312
{
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
    struct wl_region* region;

    region = wl_compositor_create_region(_glfw.wl.compositor);
    if (!region)
        return;

    wl_region_add(region, 0, 0, window->wl.width, window->wl.height);
    wl_surface_set_opaque_region(window->wl.surface, region);
    wl_surface_commit(window->wl.surface);
    wl_region_destroy(region);
}


static void resizeWindow(_GLFWwindow* window)
{
    int scale = window->wl.scale;
    int scaledWidth = window->wl.width * scale;
    int scaledHeight = window->wl.height * scale;
    wl_egl_window_resize(window->wl.native, scaledWidth, scaledHeight, 0, 0);
    if (!window->wl.transparent)
        setOpaqueRegion(window);
    _glfwInputFramebufferSize(window, scaledWidth, scaledHeight);
    _glfwInputWindowContentScale(window, scale, scale);
336

337
    if (!window->wl.decorations.top.surface)
338
339
        return;

340
    // Top decoration.
341
342
    wp_viewport_set_destination(window->wl.decorations.top.viewport,
                                window->wl.width, _GLFW_DECORATION_TOP);
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
    wl_surface_commit(window->wl.decorations.top.surface);

    // Left decoration.
    wp_viewport_set_destination(window->wl.decorations.left.viewport,
                                _GLFW_DECORATION_WIDTH, window->wl.height + _GLFW_DECORATION_TOP);
    wl_surface_commit(window->wl.decorations.left.surface);

    // Right decoration.
    wl_subsurface_set_position(window->wl.decorations.right.subsurface,
                               window->wl.width, -_GLFW_DECORATION_TOP);
    wp_viewport_set_destination(window->wl.decorations.right.viewport,
                                _GLFW_DECORATION_WIDTH, window->wl.height + _GLFW_DECORATION_TOP);
    wl_surface_commit(window->wl.decorations.right.surface);

    // Bottom decoration.
    wl_subsurface_set_position(window->wl.decorations.bottom.subsurface,
                               -_GLFW_DECORATION_WIDTH, window->wl.height);
    wp_viewport_set_destination(window->wl.decorations.bottom.viewport,
361
                                window->wl.width + _GLFW_DECORATION_HORIZONTAL, _GLFW_DECORATION_WIDTH);
362
363
364
    wl_surface_commit(window->wl.decorations.bottom.surface);
}

365
366
367
368
369
370
static void checkScaleChange(_GLFWwindow* window)
{
    int scale = 1;
    int i;
    int monitorScale;

371
    // Check if we will be able to set the buffer scale or not.
372
    if (_glfw.wl.compositorVersion < 3)
373
374
        return;

375
376
377
378
379
380
381
382
383
384
385
386
387
    // Get the scale factor from the highest scale monitor.
    for (i = 0; i < window->wl.monitorsCount; ++i)
    {
        monitorScale = window->wl.monitors[i]->wl.scale;
        if (scale < monitorScale)
            scale = monitorScale;
    }

    // Only change the framebuffer size if the scale changed.
    if (scale != window->wl.scale)
    {
        window->wl.scale = scale;
        wl_surface_set_buffer_scale(window->wl.surface, scale);
388
        resizeWindow(window);
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
    }
}

static void handleEnter(void *data,
                        struct wl_surface *surface,
                        struct wl_output *output)
{
    _GLFWwindow* window = data;
    _GLFWmonitor* monitor = wl_output_get_user_data(output);

    if (window->wl.monitorsCount + 1 > window->wl.monitorsSize)
    {
        ++window->wl.monitorsSize;
        window->wl.monitors =
            realloc(window->wl.monitors,
                    window->wl.monitorsSize * sizeof(_GLFWmonitor*));
    }

    window->wl.monitors[window->wl.monitorsCount++] = monitor;

    checkScaleChange(window);
}

static void handleLeave(void *data,
                        struct wl_surface *surface,
                        struct wl_output *output)
{
    _GLFWwindow* window = data;
    _GLFWmonitor* monitor = wl_output_get_user_data(output);
    GLFWbool found;
    int i;

    for (i = 0, found = GLFW_FALSE; i < window->wl.monitorsCount - 1; ++i)
    {
        if (monitor == window->wl.monitors[i])
            found = GLFW_TRUE;
        if (found)
            window->wl.monitors[i] = window->wl.monitors[i + 1];
    }
    window->wl.monitors[--window->wl.monitorsCount] = NULL;

    checkScaleChange(window);
}

static const struct wl_surface_listener surfaceListener = {
    handleEnter,
    handleLeave
};

438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
static void setIdleInhibitor(_GLFWwindow* window, GLFWbool enable)
{
    if (enable && !window->wl.idleInhibitor && _glfw.wl.idleInhibitManager)
    {
        window->wl.idleInhibitor =
            zwp_idle_inhibit_manager_v1_create_inhibitor(
                _glfw.wl.idleInhibitManager, window->wl.surface);
        if (!window->wl.idleInhibitor)
            _glfwInputError(GLFW_PLATFORM_ERROR,
                            "Wayland: Idle inhibitor creation failed");
    }
    else if (!enable && window->wl.idleInhibitor)
    {
        zwp_idle_inhibitor_v1_destroy(window->wl.idleInhibitor);
        window->wl.idleInhibitor = NULL;
    }
}

456
457
static GLFWbool createSurface(_GLFWwindow* window,
                              const _GLFWwndconfig* wndconfig)
458
{
459
460
    window->wl.surface = wl_compositor_create_surface(_glfw.wl.compositor);
    if (!window->wl.surface)
461
        return GLFW_FALSE;
462

463
464
465
466
    wl_surface_add_listener(window->wl.surface,
                            &surfaceListener,
                            window);

467
468
    wl_surface_set_user_data(window->wl.surface, window);

469
470
471
472
    window->wl.native = wl_egl_window_create(window->wl.surface,
                                             wndconfig->width,
                                             wndconfig->height);
    if (!window->wl.native)
473
        return GLFW_FALSE;
474

475
476
477
478
    window->wl.width = wndconfig->width;
    window->wl.height = wndconfig->height;
    window->wl.scale = 1;

479
480
    if (!window->wl.transparent)
        setOpaqueRegion(window);
481

482
483
    if (window->decorated && !window->monitor)
        createDecorations(window);
484

485
486
487
    return GLFW_TRUE;
}

488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
static void setFullscreen(_GLFWwindow* window, _GLFWmonitor* monitor, int refreshRate)
{
    if (window->wl.xdg.toplevel)
    {
        xdg_toplevel_set_fullscreen(
            window->wl.xdg.toplevel,
            monitor->wl.output);
    }
    else if (window->wl.shellSurface)
    {
        wl_shell_surface_set_fullscreen(
            window->wl.shellSurface,
            WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT,
            refreshRate * 1000, // Convert Hz to mHz.
            monitor->wl.output);
    }
    setIdleInhibitor(window, GLFW_TRUE);
505
    destroyDecorations(window);
506
507
}

508
509
static GLFWbool createShellSurface(_GLFWwindow* window)
{
510
511
512
513
514
515
516
    if (!_glfw.wl.shell)
    {
        _glfwInputError(GLFW_PLATFORM_ERROR,
                        "Wayland: wl_shell protocol not available");
        return GLFW_FALSE;
    }

517
518
519
    window->wl.shellSurface = wl_shell_get_shell_surface(_glfw.wl.shell,
                                                         window->wl.surface);
    if (!window->wl.shellSurface)
520
521
522
    {
        _glfwInputError(GLFW_PLATFORM_ERROR,
                        "Wayland: Shell surface creation failed");
523
        return GLFW_FALSE;
524
    }
525

526
    wl_shell_surface_add_listener(window->wl.shellSurface,
527
528
529
                                  &shellSurfaceListener,
                                  window);

530
    if (window->wl.title)
531
        wl_shell_surface_set_title(window->wl.shellSurface, window->wl.title);
532
533
534

    if (window->monitor)
    {
535
        setFullscreen(window, window->monitor, 0);
536
    }
537
538
    else if (window->wl.maximized)
    {
539
        wl_shell_surface_set_maximized(window->wl.shellSurface, NULL);
540
        setIdleInhibitor(window, GLFW_FALSE);
541
    }
542
543
    else
    {
544
        wl_shell_surface_set_toplevel(window->wl.shellSurface);
545
        setIdleInhibitor(window, GLFW_FALSE);
546
    }
547

548
549
    wl_surface_commit(window->wl.surface);

550
    return GLFW_TRUE;
551
552
}

553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
static void xdgToplevelHandleConfigure(void* data,
                                       struct xdg_toplevel* toplevel,
                                       int32_t width,
                                       int32_t height,
                                       struct wl_array* states)
{
    _GLFWwindow* window = data;
    float aspectRatio;
    float targetRatio;
    uint32_t* state;
    GLFWbool maximized = GLFW_FALSE;
    GLFWbool fullscreen = GLFW_FALSE;
    GLFWbool activated = GLFW_FALSE;

    wl_array_for_each(state, states)
    {
        switch (*state)
        {
            case XDG_TOPLEVEL_STATE_MAXIMIZED:
                maximized = GLFW_TRUE;
                break;
            case XDG_TOPLEVEL_STATE_FULLSCREEN:
                fullscreen = GLFW_TRUE;
                break;
            case XDG_TOPLEVEL_STATE_RESIZING:
                break;
            case XDG_TOPLEVEL_STATE_ACTIVATED:
                activated = GLFW_TRUE;
                break;
        }
    }

585
    if (width != 0 && height != 0)
586
    {
587
        if (!maximized && !fullscreen)
588
        {
589
590
591
592
593
594
595
596
597
            if (window->numer != GLFW_DONT_CARE && window->denom != GLFW_DONT_CARE)
            {
                aspectRatio = (float)width / (float)height;
                targetRatio = (float)window->numer / (float)window->denom;
                if (aspectRatio < targetRatio)
                    height = width / targetRatio;
                else if (aspectRatio > targetRatio)
                    width = height * targetRatio;
            }
598
599
        }

600
601
602
603
        _glfwInputWindowSize(window, width, height);
        _glfwPlatformSetWindowSize(window, width, height);
        _glfwInputWindowDamage(window);
    }
604

605
    if (!window->wl.justCreated && !activated && window->autoIconify)
606
607
        _glfwPlatformIconifyWindow(window);
    _glfwInputWindowFocus(window, activated);
608
    window->wl.justCreated = GLFW_FALSE;
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
}

static void xdgToplevelHandleClose(void* data,
                                   struct xdg_toplevel* toplevel)
{
    _GLFWwindow* window = data;
    _glfwInputWindowCloseRequest(window);
}

static const struct xdg_toplevel_listener xdgToplevelListener = {
    xdgToplevelHandleConfigure,
    xdgToplevelHandleClose
};

static void xdgSurfaceHandleConfigure(void* data,
                                      struct xdg_surface* surface,
                                      uint32_t serial)
{
    xdg_surface_ack_configure(surface, serial);
}

static const struct xdg_surface_listener xdgSurfaceListener = {
    xdgSurfaceHandleConfigure
};

static GLFWbool createXdgSurface(_GLFWwindow* window)
{
    window->wl.xdg.surface = xdg_wm_base_get_xdg_surface(_glfw.wl.wmBase,
                                                         window->wl.surface);
    if (!window->wl.xdg.surface)
    {
        _glfwInputError(GLFW_PLATFORM_ERROR,
                        "Wayland: xdg-surface creation failed");
        return GLFW_FALSE;
    }

    xdg_surface_add_listener(window->wl.xdg.surface,
                             &xdgSurfaceListener,
                             window);

    window->wl.xdg.toplevel = xdg_surface_get_toplevel(window->wl.xdg.surface);
    if (!window->wl.xdg.toplevel)
    {
        _glfwInputError(GLFW_PLATFORM_ERROR,
                        "Wayland: xdg-toplevel creation failed");
        return GLFW_FALSE;
    }

    xdg_toplevel_add_listener(window->wl.xdg.toplevel,
                              &xdgToplevelListener,
                              window);

    if (window->wl.title)
        xdg_toplevel_set_title(window->wl.xdg.toplevel, window->wl.title);

664
665
666
667
668
669
    if (window->minwidth != GLFW_DONT_CARE && window->minheight != GLFW_DONT_CARE)
        xdg_toplevel_set_min_size(window->wl.xdg.toplevel,
                                  window->minwidth, window->minheight);
    if (window->maxwidth != GLFW_DONT_CARE && window->maxheight != GLFW_DONT_CARE)
        xdg_toplevel_set_max_size(window->wl.xdg.toplevel,
                                  window->maxwidth, window->maxheight);
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687

    if (window->monitor)
    {
        xdg_toplevel_set_fullscreen(window->wl.xdg.toplevel,
                                    window->monitor->wl.output);
        setIdleInhibitor(window, GLFW_TRUE);
    }
    else if (window->wl.maximized)
    {
        xdg_toplevel_set_maximized(window->wl.xdg.toplevel);
        setIdleInhibitor(window, GLFW_FALSE);
    }
    else
    {
        setIdleInhibitor(window, GLFW_FALSE);
    }

    wl_surface_commit(window->wl.surface);
688
    wl_display_roundtrip(_glfw.wl.display);
689
690
691
692

    return GLFW_TRUE;
}

693
694
695
696
697
698
static void
handleEvents(int timeout)
{
    struct wl_display* display = _glfw.wl.display;
    struct pollfd fds[] = {
        { wl_display_get_fd(display), POLLIN },
699
        { _glfw.wl.timerfd, POLLIN },
700
    };
701
702
    ssize_t read_ret;
    uint64_t repeats, i;
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721

    while (wl_display_prepare_read(display) != 0)
        wl_display_dispatch_pending(display);

    // If an error different from EAGAIN happens, we have likely been
    // disconnected from the Wayland session, try to handle that the best we
    // can.
    if (wl_display_flush(display) < 0 && errno != EAGAIN)
    {
        _GLFWwindow* window = _glfw.windowListHead;
        while (window)
        {
            _glfwInputWindowCloseRequest(window);
            window = window->next;
        }
        wl_display_cancel_read(display);
        return;
    }

722
    if (poll(fds, 2, timeout) > 0)
723
    {
724
725
726
727
728
729
730
731
732
733
734
735
        if (fds[0].revents & POLLIN)
        {
            wl_display_read_events(display);
            wl_display_dispatch_pending(display);
        }
        else
        {
            wl_display_cancel_read(display);
        }

        if (fds[1].revents & POLLIN)
        {
736
737
738
739
740
741
742
743
            read_ret = read(_glfw.wl.timerfd, &repeats, sizeof(repeats));
            if (read_ret != 8)
                return;

            for (i = 0; i < repeats; ++i)
                _glfwInputKey(_glfw.wl.keyboardFocus, _glfw.wl.keyboardLastKey,
                              _glfw.wl.keyboardLastScancode, GLFW_REPEAT,
                              _glfw.wl.xkb.modifiers);
744
        }
745
746
747
748
749
750
751
    }
    else
    {
        wl_display_cancel_read(display);
    }
}

752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
// Translates a GLFW standard cursor to a theme cursor name
//
static char *translateCursorShape(int shape)
{
    switch (shape)
    {
        case GLFW_ARROW_CURSOR:
            return "left_ptr";
        case GLFW_IBEAM_CURSOR:
            return "xterm";
        case GLFW_CROSSHAIR_CURSOR:
            return "crosshair";
        case GLFW_HAND_CURSOR:
            return "grabbing";
        case GLFW_HRESIZE_CURSOR:
            return "sb_h_double_arrow";
        case GLFW_VRESIZE_CURSOR:
            return "sb_v_double_arrow";
    }
    return NULL;
}

774
775
776
777
778
779
780
781
782
//////////////////////////////////////////////////////////////////////////
//////                       GLFW platform API                      //////
//////////////////////////////////////////////////////////////////////////

int _glfwPlatformCreateWindow(_GLFWwindow* window,
                              const _GLFWwndconfig* wndconfig,
                              const _GLFWctxconfig* ctxconfig,
                              const _GLFWfbconfig* fbconfig)
{
783
    window->wl.justCreated = GLFW_TRUE;
784
785
    window->wl.transparent = fbconfig->transparent;

786
787
788
    if (!createSurface(window, wndconfig))
        return GLFW_FALSE;

789
    if (ctxconfig->client != GLFW_NO_API)
790
    {
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
        if (ctxconfig->source == GLFW_EGL_CONTEXT_API ||
            ctxconfig->source == GLFW_NATIVE_CONTEXT_API)
        {
            if (!_glfwInitEGL())
                return GLFW_FALSE;
            if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig))
                return GLFW_FALSE;
        }
        else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API)
        {
            if (!_glfwInitOSMesa())
                return GLFW_FALSE;
            if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig))
                return GLFW_FALSE;
        }
806
    }
807

808
    if (wndconfig->title)
809
        window->wl.title = _glfw_strdup(wndconfig->title);
810
811

    if (wndconfig->visible)
812
    {
813
814
815
816
817
818
819
820
821
822
        if (_glfw.wl.wmBase)
        {
            if (!createXdgSurface(window))
                return GLFW_FALSE;
        }
        else
        {
            if (!createShellSurface(window))
                return GLFW_FALSE;
        }
823
824

        window->wl.visible = GLFW_TRUE;
825
826
827
    }
    else
    {
828
829
        window->wl.xdg.surface = NULL;
        window->wl.xdg.toplevel = NULL;
830
        window->wl.shellSurface = NULL;
831
        window->wl.visible = GLFW_FALSE;
832
833
    }

834
835
    window->wl.currentCursor = NULL;

836
837
838
839
    window->wl.monitors = calloc(1, sizeof(_GLFWmonitor*));
    window->wl.monitorsCount = 0;
    window->wl.monitorsSize = 1;

840
    return GLFW_TRUE;
841
842
843
844
}

void _glfwPlatformDestroyWindow(_GLFWwindow* window)
{
845
846
847
    if (window == _glfw.wl.pointerFocus)
    {
        _glfw.wl.pointerFocus = NULL;
848
        _glfwInputCursorEnter(window, GLFW_FALSE);
849
850
851
852
    }
    if (window == _glfw.wl.keyboardFocus)
    {
        _glfw.wl.keyboardFocus = NULL;
853
        _glfwInputWindowFocus(window, GLFW_FALSE);
854
855
    }

856
857
858
    if (window->wl.idleInhibitor)
        zwp_idle_inhibitor_v1_destroy(window->wl.idleInhibitor);

859
    if (window->context.destroy)
Camilla Berglund's avatar
Cleanup    
Camilla Berglund committed
860
        window->context.destroy(window);
861

862
    destroyDecorations(window);
863
864
    if (window->wl.decorations.buffer)
        wl_buffer_destroy(window->wl.decorations.buffer);
865

866
867
    if (window->wl.native)
        wl_egl_window_destroy(window->wl.native);
868

869
870
    if (window->wl.shellSurface)
        wl_shell_surface_destroy(window->wl.shellSurface);
871

872
873
874
875
876
877
    if (window->wl.xdg.toplevel)
        xdg_toplevel_destroy(window->wl.xdg.toplevel);

    if (window->wl.xdg.surface)
        xdg_surface_destroy(window->wl.xdg.surface);

878
879
    if (window->wl.surface)
        wl_surface_destroy(window->wl.surface);
880

881
    free(window->wl.title);
882
    free(window->wl.monitors);
883
884
885
886
}

void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title)
{
887
888
    if (window->wl.title)
        free(window->wl.title);
889
    window->wl.title = _glfw_strdup(title);
890
891
892
    if (window->wl.xdg.toplevel)
        xdg_toplevel_set_title(window->wl.xdg.toplevel, title);
    else if (window->wl.shellSurface)
893
        wl_shell_surface_set_title(window->wl.shellSurface, title);
894
895
}

Camilla Berglund's avatar
Camilla Berglund committed
896
897
898
void _glfwPlatformSetWindowIcon(_GLFWwindow* window,
                                int count, const GLFWimage* images)
{
899
900
    _glfwInputError(GLFW_PLATFORM_ERROR,
                    "Wayland: Setting window icon not supported");
Camilla Berglund's avatar
Camilla Berglund committed
901
902
}

903
904
void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos)
{
905
906
    // A Wayland client is not aware of its position, so just warn and leave it
    // as (0, 0)
907
908

    _glfwInputError(GLFW_PLATFORM_ERROR,
909
                    "Wayland: Window position retrieval not supported");
910
911
912
913
}

void _glfwPlatformSetWindowPos(_GLFWwindow* window, int xpos, int ypos)
{
Camilla Berglund's avatar
Camilla Berglund committed
914
    // A Wayland client can not set its position, so just warn
915
916

    _glfwInputError(GLFW_PLATFORM_ERROR,
917
                    "Wayland: Window position setting not supported");
918
919
920
921
922
}

void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height)
{
    if (width)
923
        *width = window->wl.width;
924
    if (height)
925
        *height = window->wl.height;
926
927
928
929
}

void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height)
{
930
931
    window->wl.width = width;
    window->wl.height = height;
932
    resizeWindow(window);
933
934
}

935
936
937
938
void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window,
                                      int minwidth, int minheight,
                                      int maxwidth, int maxheight)
{
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
    if (_glfw.wl.wmBase)
    {
        if (window->wl.xdg.toplevel)
        {
            if (minwidth == GLFW_DONT_CARE || minheight == GLFW_DONT_CARE)
                minwidth = minheight = 0;
            if (maxwidth == GLFW_DONT_CARE || maxheight == GLFW_DONT_CARE)
                maxwidth = maxheight = 0;
            xdg_toplevel_set_min_size(window->wl.xdg.toplevel, minwidth, minheight);
            xdg_toplevel_set_max_size(window->wl.xdg.toplevel, maxwidth, maxheight);
            wl_surface_commit(window->wl.surface);
        }
    }
    else
    {
        // TODO: find out how to trigger a resize.
        // The actual limits are checked in the wl_shell_surface::configure handler.
    }
957
958
959
960
}

void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int numer, int denom)
{
961
962
    // TODO: find out how to trigger a resize.
    // The actual limits are checked in the wl_shell_surface::configure handler.
963
964
}

965
966
967
void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height)
{
    _glfwPlatformGetWindowSize(window, width, height);
968
969
    *width *= window->wl.scale;
    *height *= window->wl.scale;
970
971
}

972
973
974
975
void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window,
                                     int* left, int* top,
                                     int* right, int* bottom)
{
976
977
978
979
980
981
982
983
984
985
986
    if (window->decorated && !window->monitor)
    {
        if (top)
            *top = _GLFW_DECORATION_TOP;
        if (left)
            *left = _GLFW_DECORATION_WIDTH;
        if (right)
            *right = _GLFW_DECORATION_WIDTH;
        if (bottom)
            *bottom = _GLFW_DECORATION_WIDTH;
    }
987
988
}

Camilla Löwy's avatar
Camilla Löwy committed
989
990
991
992
993
994
995
996
997
void _glfwPlatformGetWindowContentScale(_GLFWwindow* window,
                                        float* xscale, float* yscale)
{
    if (xscale)
        *xscale = (float) window->wl.scale;
    if (yscale)
        *yscale = (float) window->wl.scale;
}

998
999
void _glfwPlatformIconifyWindow(_GLFWwindow* window)
{
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
    if (_glfw.wl.wmBase)
    {
        if (window->wl.xdg.toplevel)
            xdg_toplevel_set_minimized(window->wl.xdg.toplevel);
    }
    else
    {
        _glfwInputError(GLFW_PLATFORM_ERROR,
                        "Wayland: Iconify window not supported on wl_shell");
    }
1010
1011
1012
1013
}

void _glfwPlatformRestoreWindow(_GLFWwindow* window)
{
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
    if (window->wl.xdg.toplevel)
    {
        if (window->monitor)
            xdg_toplevel_unset_fullscreen(window->wl.xdg.toplevel);
        if (window->wl.maximized)
            xdg_toplevel_unset_maximized(window->wl.xdg.toplevel);
        // There is no way to unset minimized, or even to know if we are
        // minimized, so there is nothing to do here.
    }
    else if (window->wl.shellSurface)
1024
    {
1025
        if (window->monitor || window->wl.maximized)
1026
            wl_shell_surface_set_toplevel(window->wl.shellSurface);
1027
    }
1028
1029
    _glfwInputWindowMonitor(window, NULL);
    window->wl.maximized = GLFW_FALSE;
1030
1031
}

1032
1033
void _glfwPlatformMaximizeWindow(_GLFWwindow* window)
{
1034
    if (window->wl.xdg.toplevel)
1035
    {
1036
        xdg_toplevel_set_maximized(window->wl.xdg.toplevel);
1037
    }
1038
1039
1040
1041
1042
1043
    else if (window->wl.shellSurface)
    {
        // Let the compositor select the best output.
        wl_shell_surface_set_maximized(window->wl.shellSurface, NULL);
    }
    window->wl.maximized = GLFW_TRUE;
1044
1045
}

1046
1047
void _glfwPlatformShowWindow(_GLFWwindow* window)
{
1048
1049
1050
1051
1052
1053
1054
1055
    if (!window->wl.visible)
    {
        if (_glfw.wl.wmBase)
            createXdgSurface(window);
        else if (!window->wl.shellSurface)
            createShellSurface(window);
        window->wl.visible = GLFW_TRUE;
    }
1056
1057
1058
1059
}

void _glfwPlatformHideWindow(_GLFWwindow* window)
{
1060
    if (window->wl.xdg.toplevel)
1061
    {
1062
1063
1064
1065
1066
1067
1068
1069
1070
        xdg_toplevel_destroy(window->wl.xdg.toplevel);
        xdg_surface_destroy(window->wl.xdg.surface);
        window->wl.xdg.toplevel = NULL;
        window->wl.xdg.surface = NULL;
    }
    else if (window->wl.shellSurface)
    {
        wl_shell_surface_destroy(window->wl.shellSurface);
        window->wl.shellSurface = NULL;
1071
    }
1072
    window->wl.visible = GLFW_FALSE;
1073
1074
}

1075
1076
void _glfwPlatformRequestWindowAttention(_GLFWwindow* window)
{
Camilla Löwy's avatar
Cleanup    
Camilla Löwy committed
1077
1078
1079
    // TODO
    _glfwInputError(GLFW_PLATFORM_ERROR,
                    "Wayland: Window attention request not implemented yet");
1080
1081
}

Camilla Berglund's avatar
Camilla Berglund committed
1082
1083
void _glfwPlatformFocusWindow(_GLFWwindow* window)
{
1084
1085
    _glfwInputError(GLFW_PLATFORM_ERROR,
                    "Wayland: Focusing a window requires user interaction");
Camilla Berglund's avatar
Camilla Berglund committed
1086
1087
}

Camilla Berglund's avatar
Camilla Berglund committed
1088
1089
1090
1091
1092
1093
void _glfwPlatformSetWindowMonitor(_GLFWwindow* window,
                                   _GLFWmonitor* monitor,
                                   int xpos, int ypos,
                                   int width, int height,
                                   int refreshRate)
{
1094
    if (monitor)
1095
    {
1096
        setFullscreen(window, monitor, refreshRate);
1097
    }
1098
    else
1099
    {
1100
1101
1102
        if (window->wl.xdg.toplevel)
            xdg_toplevel_unset_fullscreen(window->wl.xdg.toplevel);
        else if (window->wl.shellSurface)
1103
            wl_shell_surface_set_toplevel(window->wl.shellSurface);
1104
        setIdleInhibitor(window, GLFW_FALSE);
1105
1106
        if (window->decorated)
            createDecorations(window);
1107
    }
Camilla Löwy's avatar
Cleanup    
Camilla Löwy committed
1108
    _glfwInputWindowMonitor(window, monitor);
Camilla Berglund's avatar
Camilla Berglund committed
1109
1110
}

1111
1112
int _glfwPlatformWindowFocused(_GLFWwindow* window)
{
1113
    return _glfw.wl.keyboardFocus == window;
1114
1115
1116
1117
}

int _glfwPlatformWindowIconified(_GLFWwindow* window)
{
1118
1119
    // wl_shell doesn't have any iconified concept, and xdg-shell doesn’t give
    // any way to request whether a surface is iconified.
1120
    return GLFW_FALSE;
1121
1122
1123
1124
}

int _glfwPlatformWindowVisible(_GLFWwindow* window)
{
1125
    return window->wl.visible;
1126
1127
}

1128
1129
int _glfwPlatformWindowMaximized(_GLFWwindow* window)
{
1130
    return window->wl.maximized;
1131
1132
}

1133
1134
1135
1136
1137
int _glfwPlatformWindowHovered(_GLFWwindow* window)
{
    return window->wl.hovered;
}

1138
1139
int _glfwPlatformFramebufferTransparent(_GLFWwindow* window)
{
1140
    return window->wl.transparent;
1141
1142
}

Camilla Löwy's avatar
Camilla Löwy committed
1143
1144
1145
1146
1147
1148
1149
1150
1151
void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled)
{
    // TODO
    _glfwInputError(GLFW_PLATFORM_ERROR,
                    "Wayland: Window attribute setting not implemented yet");
}

void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled)
{
1152
1153
1154
1155
1156
1157
1158
    if (!window->monitor)
    {
        if (enabled)
            createDecorations(window);
        else
            destroyDecorations(window);
    }
Camilla Löwy's avatar
Camilla Löwy committed
1159
1160
1161
1162
1163
1164
1165
1166
1167
}

void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled)
{
    // TODO
    _glfwInputError(GLFW_PLATFORM_ERROR,
                    "Wayland: Window attribute setting not implemented yet");
}

1168
1169
1170
1171
1172
1173
1174
1175
1176
float _glfwPlatformGetWindowOpacity(_GLFWwindow* window)
{
    return 1.f;
}

void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity)
{
}

1177
1178
void _glfwPlatformPollEvents(void)
{
1179
    handleEvents(0);
1180
1181
1182
1183
}

void _glfwPlatformWaitEvents(void)
{
1184
    handleEvents(-1);
1185
1186
}

Camilla Berglund's avatar
Camilla Berglund committed
1187
1188
1189
1190
1191
void _glfwPlatformWaitEventsTimeout(double timeout)
{
    handleEvents((int) (timeout * 1e3));
}

1192
1193
void _glfwPlatformPostEmptyEvent(void)
{
1194
    wl_display_sync(_glfw.wl.display);
1195
1196
}

1197
1198
void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos)
{
1199
    if (xpos)
1200
        *xpos = window->wl.cursorPosX;
1201
    if (ypos)
1202
        *ypos = window->wl.cursorPosY;
1203
1204
}

1205
1206
static GLFWbool isPointerLocked(_GLFWwindow* window);

1207
1208
void _glfwPlatformSetCursorPos(_GLFWwindow* window, double x, double y)
{
1209
1210
1211
1212
1213
1214
1215
    if (isPointerLocked(window))
    {
        zwp_locked_pointer_v1_set_cursor_position_hint(
            window->wl.pointerLock.lockedPointer,
            wl_fixed_from_double(x), wl_fixed_from_double(y));
        wl_surface_commit(window->wl.surface);
    }
1216
1217
}

1218
void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode)
1219
{
1220
    _glfwPlatformSetCursor(window, window->wl.currentCursor);
1221
}
Camilla Berglund's avatar
Camilla Berglund committed
1222

Camilla Löwy's avatar
Cleanup    
Camilla Löwy committed
1223
const char* _glfwPlatformGetScancodeName(int scancode)
Camilla Berglund's avatar
Camilla Berglund committed
1224
1225
1226
1227
1228
{
    // TODO
    return NULL;
}

Camilla Berglund's avatar
Cleanup    
Camilla Berglund committed
1229
int _glfwPlatformGetKeyScancode(int key)
Michael Stocker's avatar
Michael Stocker committed
1230
{
Camilla Berglund's avatar
Cleanup    
Camilla Berglund committed
1231
    return _glfw.wl.scancodes[key];
Michael Stocker's avatar
Michael Stocker committed
1232
1233
}

1234
1235
1236
1237
int _glfwPlatformCreateCursor(_GLFWcursor* cursor,
                              const GLFWimage* image,
                              int xhot, int yhot)
{
1238
    cursor->wl.buffer = createShmBuffer(image);
1239
1240
1241
1242
    cursor->wl.width = image->width;
    cursor->wl.height = image->height;
    cursor->wl.xhot = xhot;
    cursor->wl.yhot = yhot;
1243
    return GLFW_TRUE;
1244
1245
}

1246
1247
int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape)
{
Camilla Berglund's avatar
Cleanup    
Camilla Berglund committed
1248
    struct wl_cursor* standardCursor;
1249

Camilla Berglund's avatar
Cleanup    
Camilla Berglund committed
1250
1251
1252
1253
    standardCursor = wl_cursor_theme_get_cursor(_glfw.wl.cursorTheme,
                                                translateCursorShape(shape));
    if (!standardCursor)
    {
1254
1255
1256
1257
1258
1259
        _glfwInputError(GLFW_PLATFORM_ERROR,
                        "Wayland: Standard cursor \"%s\" not found",
                        translateCursorShape(shape));
        return GLFW_FALSE;
    }

Camilla Berglund's avatar
Cleanup    
Camilla Berglund committed
1260
    cursor->wl.image = standardCursor->images[0];
1261
    return GLFW_TRUE;
1262
1263
}

1264
1265
void _glfwPlatformDestroyCursor(_GLFWcursor* cursor)
{
1266
1267
1268
1269
    // If it's a standard cursor we don't need to do anything here
    if (cursor->wl.image)
        return;

1270
1271
    if (cursor->wl.buffer)
        wl_buffer_destroy(cursor->wl.buffer);
1272
1273
}

1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
static void handleRelativeMotion(void* data,
                                 struct zwp_relative_pointer_v1* pointer,
                                 uint32_t timeHi,
                                 uint32_t timeLo,
                                 wl_fixed_t dx,
                                 wl_fixed_t dy,
                                 wl_fixed_t dxUnaccel,
                                 wl_fixed_t dyUnaccel)
{
    _GLFWwindow* window = data;

    if (window->cursorMode != GLFW_CURSOR_DISABLED)
        return;

1288
1289
1290
    _glfwInputCursorPos(window,
                        window->virtualCursorPosX + wl_fixed_to_double(dxUnaccel),
                        window->virtualCursorPosY + wl_fixed_to_double(dyUnaccel));
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
}

static const struct zwp_relative_pointer_v1_listener relativePointerListener = {
    handleRelativeMotion
};

static void handleLocked(void* data,
                         struct zwp_locked_pointer_v1* lockedPointer)
{
}

static void unlockPointer(_GLFWwindow* window)
{
    struct zwp_relative_pointer_v1* relativePointer =
        window->wl.pointerLock.relativePointer;
    struct zwp_locked_pointer_v1* lockedPointer =
        window->wl.pointerLock.lockedPointer;

    zwp_relative_pointer_v1_destroy(relativePointer);
    zwp_locked_pointer_v1_destroy(lockedPointer);

    window->wl.pointerLock.relativePointer = NULL;
    window->wl.pointerLock.lockedPointer = NULL;
}

static void lockPointer(_GLFWwindow* window);

static void handleUnlocked(void* data,
                           struct zwp_locked_pointer_v1* lockedPointer)
{
}

static const struct zwp_locked_pointer_v1_listener lockedPointerListener = {
    handleLocked,
    handleUnlocked
};

static void lockPointer(_GLFWwindow* window)
{
    struct zwp_relative_pointer_v1* relativePointer;
    struct zwp_locked_pointer_v1* lockedPointer;

    if (!_glfw.wl.relativePointerManager)
    {
        _glfwInputError(GLFW_PLATFORM_ERROR,
                        "Wayland: no relative pointer manager");
        return;
    }

    relativePointer =
        zwp_relative_pointer_manager_v1_get_relative_pointer(
            _glfw.wl.relativePointerManager,
            _glfw.wl.pointer);
    zwp_relative_pointer_v1_add_listener(relativePointer,
                                         &relativePointerListener,
                                         window);

    lockedPointer =
        zwp_pointer_constraints_v1_lock_pointer(
            _glfw.wl.pointerConstraints,
            window->wl.surface,
            _glfw.wl.pointer,
            NULL,
            ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT);
    zwp_locked_pointer_v1_add_listener(lockedPointer,
                                       &lockedPointerListener,
                                       window);

    window->wl.pointerLock.relativePointer = relativePointer;
    window->wl.pointerLock.lockedPointer = lockedPointer;

    wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.pointerSerial,
                          NULL, 0, 0);
}

static GLFWbool isPointerLocked(_GLFWwindow* window)
{
    return window->wl.pointerLock.lockedPointer != NULL;
}

1371
1372
void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor)
{
Jonas Ådahl's avatar
Jonas Ådahl committed
1373
    struct wl_buffer* buffer;
1374
    struct wl_cursor* defaultCursor;
Jonas Ådahl's avatar
Jonas Ådahl committed
1375
1376
    struct wl_cursor_image* image;
    struct wl_surface* surface = _glfw.wl.cursorSurface;
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387

    if (!_glfw.wl.pointer)
        return;

    window->wl.currentCursor = cursor;

    // If we're not in the correct window just save the cursor
    // the next time the pointer enters the window the cursor will change
    if (window != _glfw.wl.pointerFocus)
        return;

1388
1389
1390
1391
    // Unlock possible pointer lock if no longer disabled.
    if (window->cursorMode != GLFW_CURSOR_DISABLED && isPointerLocked(window))
        unlockPointer(window);

1392
1393
    if (window->cursorMode == GLFW_CURSOR_NORMAL)
    {
Camilla Berglund's avatar
Cleanup    
Camilla Berglund committed
1394
1395
1396
        if (cursor)
            image = cursor->wl.image;
        else