wl_window.c 26.3 KB
Newer Older
1
//========================================================================
Camilla Berglund's avatar
Camilla Berglund committed
2
// GLFW 3.2 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 <poll.h>
39

40
#include <wayland-egl.h>
41
#include <wayland-cursor.h>
42
43


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

Camilla Berglund's avatar
Camilla Berglund committed
51
52
53
54
55
static void handleConfigure(void* data,
                            struct wl_shell_surface* shellSurface,
                            uint32_t edges,
                            int32_t width,
                            int32_t height)
56
{
57
58
59
60
61
    _GLFWwindow* window = data;
    _glfwInputFramebufferSize(window, width, height);
    _glfwInputWindowSize(window, width, height);
    _glfwPlatformSetWindowSize(window, width, height);
    _glfwInputWindowDamage(window);
62
63
}

Camilla Berglund's avatar
Camilla Berglund committed
64
65
static void handlePopupDone(void* data,
                            struct wl_shell_surface* shellSurface)
66
67
68
69
70
71
72
73
74
{
}

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

75
76
77
78
79
80
81
static void checkScaleChange(_GLFWwindow* window)
{
    int scaledWidth, scaledHeight;
    int scale = 1;
    int i;
    int monitorScale;

82
    // Check if we will be able to set the buffer scale or not.
83
    if (_glfw.wl.wl_compositor_version < 3)
84
85
        return;

86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
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
    // 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;
        scaledWidth = window->wl.width * scale;
        scaledHeight = window->wl.height * scale;
        wl_surface_set_buffer_scale(window->wl.surface, scale);
        wl_egl_window_resize(window->wl.native, scaledWidth, scaledHeight, 0, 0);
        _glfwInputFramebufferSize(window, scaledWidth, scaledHeight);
    }
}

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

152
153
static GLFWbool createSurface(_GLFWwindow* window,
                              const _GLFWwndconfig* wndconfig)
154
{
155
156
    window->wl.surface = wl_compositor_create_surface(_glfw.wl.compositor);
    if (!window->wl.surface)
157
        return GLFW_FALSE;
158

159
160
161
162
    wl_surface_add_listener(window->wl.surface,
                            &surfaceListener,
                            window);

163
164
    wl_surface_set_user_data(window->wl.surface, window);

165
166
167
168
    window->wl.native = wl_egl_window_create(window->wl.surface,
                                             wndconfig->width,
                                             wndconfig->height);
    if (!window->wl.native)
169
        return GLFW_FALSE;
170

171
172
173
    window->wl.shell_surface = wl_shell_get_shell_surface(_glfw.wl.shell,
                                                          window->wl.surface);
    if (!window->wl.shell_surface)
174
        return GLFW_FALSE;
175

176
    wl_shell_surface_add_listener(window->wl.shell_surface,
177
178
179
                                  &shellSurfaceListener,
                                  window);

180
181
    window->wl.width = wndconfig->width;
    window->wl.height = wndconfig->height;
182
    window->wl.scale = 1;
183

184
    return GLFW_TRUE;
185
186
}

187
static int
188
createTmpfileCloexec(char* tmpname)
189
190
191
192
193
194
195
196
197
198
{
    int fd;

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

    return fd;
}

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
231
232
233
234
235
static void
handleEvents(int timeout)
{
    struct wl_display* display = _glfw.wl.display;
    struct pollfd fds[] = {
        { wl_display_get_fd(display), POLLIN },
    };

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

    if (poll(fds, 1, timeout) > 0)
    {
        wl_display_read_events(display);
        wl_display_dispatch_pending(display);
    }
    else
    {
        wl_display_cancel_read(display);
    }
}

236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
/*
 * 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.
 *
251
252
253
254
 * 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.
255
256
 */
int
257
createAnonymousFile(off_t size)
258
259
{
    static const char template[] = "/glfw-shared-XXXXXX";
260
261
    const char* path;
    char* name;
262
263
264
265
    int fd;
    int ret;

    path = getenv("XDG_RUNTIME_DIR");
266
267
    if (!path)
    {
268
269
270
271
        errno = ENOENT;
        return -1;
    }

272
    name = calloc(strlen(path) + sizeof(template), 1);
273
274
275
    strcpy(name, path);
    strcat(name, template);

276
    fd = createTmpfileCloexec(name);
277
278
279
280
281
282

    free(name);

    if (fd < 0)
        return -1;
    ret = posix_fallocate(fd, 0, size);
283
284
    if (ret != 0)
    {
285
286
287
288
289
290
        close(fd);
        errno = ret;
        return -1;
    }
    return fd;
}
291

292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
// 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;
}

314
315
316
317
318
319
320
321
322
//////////////////////////////////////////////////////////////////////////
//////                       GLFW platform API                      //////
//////////////////////////////////////////////////////////////////////////

int _glfwPlatformCreateWindow(_GLFWwindow* window,
                              const _GLFWwndconfig* wndconfig,
                              const _GLFWctxconfig* ctxconfig,
                              const _GLFWfbconfig* fbconfig)
{
323
324
325
    if (!createSurface(window, wndconfig))
        return GLFW_FALSE;

326
327
    if (ctxconfig->api != GLFW_NO_API)
    {
328
        if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig))
329
330
            return GLFW_FALSE;
    }
331
332
333

    if (wndconfig->monitor)
    {
Camilla Berglund's avatar
Camilla Berglund committed
334
335
336
337
338
        wl_shell_surface_set_fullscreen(
            window->wl.shell_surface,
            WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT,
            0,
            wndconfig->monitor->wl.output);
339
340
341
    }
    else
    {
342
        wl_shell_surface_set_toplevel(window->wl.shell_surface);
343
344
    }

345
346
    window->wl.currentCursor = NULL;

347
348
349
350
    window->wl.monitors = calloc(1, sizeof(_GLFWmonitor*));
    window->wl.monitorsCount = 0;
    window->wl.monitorsSize = 1;

351
    return GLFW_TRUE;
352
353
354
355
}

void _glfwPlatformDestroyWindow(_GLFWwindow* window)
{
356
357
358
    if (window == _glfw.wl.pointerFocus)
    {
        _glfw.wl.pointerFocus = NULL;
359
        _glfwInputCursorEnter(window, GLFW_FALSE);
360
361
362
363
    }
    if (window == _glfw.wl.keyboardFocus)
    {
        _glfw.wl.keyboardFocus = NULL;
364
        _glfwInputWindowFocus(window, GLFW_FALSE);
365
366
    }

367
    _glfwDestroyContextEGL(window);
368

369
370
    if (window->wl.native)
        wl_egl_window_destroy(window->wl.native);
371

372
373
    if (window->wl.shell_surface)
        wl_shell_surface_destroy(window->wl.shell_surface);
374

375
376
    if (window->wl.surface)
        wl_surface_destroy(window->wl.surface);
377
378

    free(window->wl.monitors);
379
380
381
382
}

void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title)
{
383
    wl_shell_surface_set_title(window->wl.shell_surface, title);
384
385
386
387
}

void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos)
{
388
389
    // A Wayland client is not aware of its position, so just warn and leave it
    // as (0, 0)
390
391

    _glfwInputError(GLFW_PLATFORM_ERROR,
392
                    "Wayland: Window position retrieval not supported");
393
394
395
396
}

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

    _glfwInputError(GLFW_PLATFORM_ERROR,
400
                    "Wayland: Window position setting not supported");
401
402
403
404
405
}

void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height)
{
    if (width)
406
        *width = window->wl.width;
407
    if (height)
408
        *height = window->wl.height;
409
410
411
412
}

void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height)
{
413
414
    int scaledWidth = width * window->wl.scale;
    int scaledHeight = height * window->wl.scale;
415
416
    window->wl.width = width;
    window->wl.height = height;
417
418
    wl_egl_window_resize(window->wl.native, scaledWidth, scaledHeight, 0, 0);
    _glfwInputFramebufferSize(window, scaledWidth, scaledHeight);
419
420
}

421
422
423
424
425
426
427
428
429
430
431
432
433
434
void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window,
                                      int minwidth, int minheight,
                                      int maxwidth, int maxheight)
{
    // TODO
    fprintf(stderr, "_glfwPlatformSetWindowSizeLimits not implemented yet\n");
}

void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int numer, int denom)
{
    // TODO
    fprintf(stderr, "_glfwPlatformSetWindowAspectRatio not implemented yet\n");
}

435
436
437
void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height)
{
    _glfwPlatformGetWindowSize(window, width, height);
438
439
    *width *= window->wl.scale;
    *height *= window->wl.scale;
440
441
}

442
443
444
445
446
447
448
449
void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window,
                                     int* left, int* top,
                                     int* right, int* bottom)
{
    // TODO
    fprintf(stderr, "_glfwPlatformGetWindowFrameSize not implemented yet\n");
}

450
451
452
453
454
455
456
457
458
459
460
461
void _glfwPlatformIconifyWindow(_GLFWwindow* window)
{
    // TODO
    fprintf(stderr, "_glfwPlatformIconifyWindow not implemented yet\n");
}

void _glfwPlatformRestoreWindow(_GLFWwindow* window)
{
    // TODO
    fprintf(stderr, "_glfwPlatformRestoreWindow not implemented yet\n");
}

462
463
464
465
466
467
void _glfwPlatformMaximizeWindow(_GLFWwindow* window)
{
    // TODO
    fprintf(stderr, "_glfwPlatformMaximizeWindow not implemented yet\n");
}

468
469
void _glfwPlatformShowWindow(_GLFWwindow* window)
{
470
    wl_shell_surface_set_toplevel(window->wl.shell_surface);
471
472
}

473
474
475
476
477
478
void _glfwPlatformUnhideWindow(_GLFWwindow* window)
{
    // TODO
    fprintf(stderr, "_glfwPlatformUnhideWindow not implemented yet\n");
}

479
480
void _glfwPlatformHideWindow(_GLFWwindow* window)
{
481
482
    wl_surface_attach(window->wl.surface, NULL, 0, 0);
    wl_surface_commit(window->wl.surface);
483
484
}

485
486
487
int _glfwPlatformWindowFocused(_GLFWwindow* window)
{
    // TODO
488
    return GLFW_FALSE;
489
490
491
492
493
}

int _glfwPlatformWindowIconified(_GLFWwindow* window)
{
    // TODO
494
    return GLFW_FALSE;
495
496
497
498
499
}

int _glfwPlatformWindowVisible(_GLFWwindow* window)
{
    // TODO
500
    return GLFW_FALSE;
501
502
}

503
504
505
506
507
508
int _glfwPlatformWindowMaximized(_GLFWwindow* window)
{
    // TODO
    return GLFW_FALSE;
}

509
510
void _glfwPlatformPollEvents(void)
{
511
    handleEvents(0);
512
513
514
515
}

void _glfwPlatformWaitEvents(void)
{
516
    handleEvents(-1);
517
518
519
520
}

void _glfwPlatformPostEmptyEvent(void)
{
521
    wl_display_sync(_glfw.wl.display);
522
523
}

524
525
void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos)
{
526
    if (xpos)
527
        *xpos = window->wl.cursorPosX;
528
    if (ypos)
529
        *ypos = window->wl.cursorPosY;
530
531
}

532
533
static GLFWbool isPointerLocked(_GLFWwindow* window);

534
535
void _glfwPlatformSetCursorPos(_GLFWwindow* window, double x, double y)
{
536
537
538
539
540
541
542
    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);
    }
543
544
}

545
void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode)
546
{
547
    _glfwPlatformSetCursor(window, window->wl.currentCursor);
548
}
Camilla Berglund's avatar
Camilla Berglund committed
549

Camilla Berglund's avatar
Camilla Berglund committed
550
551
552
553
554
555
const char* _glfwPlatformGetKeyName(int key, int scancode)
{
    // TODO
    return NULL;
}

556
557
558
559
int _glfwPlatformCreateCursor(_GLFWcursor* cursor,
                              const GLFWimage* image,
                              int xhot, int yhot)
{
Jonas Ådahl's avatar
Jonas Ådahl committed
560
    struct wl_shm_pool* pool;
561
562
    int stride = image->width * 4;
    int length = image->width * image->height * 4;
Jonas Ådahl's avatar
Jonas Ådahl committed
563
    void* data;
564
565
    int fd, i;

566
    fd = createAnonymousFile(length);
Jonas Ådahl's avatar
Jonas Ådahl committed
567
568
    if (fd < 0)
    {
569
        _glfwInputError(GLFW_PLATFORM_ERROR,
Jonas Ådahl's avatar
Jonas Ådahl committed
570
571
                        "Wayland: Creating a buffer file for %d B failed: %m\n",
                        length);
572
        return GLFW_FALSE;
573
574
575
    }

    data = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
Jonas Ådahl's avatar
Jonas Ådahl committed
576
577
    if (data == MAP_FAILED)
    {
578
        _glfwInputError(GLFW_PLATFORM_ERROR,
Jonas Ådahl's avatar
Jonas Ådahl committed
579
                        "Wayland: Cursor mmap failed: %m\n");
580
        close(fd);
581
        return GLFW_FALSE;
582
583
584
585
586
587
588
589
590
    }

    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)
    {
591
592
593
594
595
596
        unsigned char alpha = source[3];

        *target++ = _glfwMultiplyAlpha(alpha, source[2]);
        *target++ = _glfwMultiplyAlpha(alpha, source[1]);
        *target++ = _glfwMultiplyAlpha(alpha, source[0]);
        *target++ = alpha;
597
598
    }

Jonas Ådahl's avatar
Jonas Ådahl committed
599
600
601
602
603
    cursor->wl.buffer =
        wl_shm_pool_create_buffer(pool, 0,
                                  image->width,
                                  image->height,
                                  stride, WL_SHM_FORMAT_ARGB8888);
604
605
606
607
608
609
610
    munmap(data, length);
    wl_shm_pool_destroy(pool);

    cursor->wl.width = image->width;
    cursor->wl.height = image->height;
    cursor->wl.xhot = xhot;
    cursor->wl.yhot = yhot;
611
    return GLFW_TRUE;
612
613
}

614
615
int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape)
{
Camilla Berglund's avatar
Cleanup    
Camilla Berglund committed
616
    struct wl_cursor* standardCursor;
617

Camilla Berglund's avatar
Cleanup    
Camilla Berglund committed
618
619
620
621
    standardCursor = wl_cursor_theme_get_cursor(_glfw.wl.cursorTheme,
                                                translateCursorShape(shape));
    if (!standardCursor)
    {
622
623
624
625
626
627
        _glfwInputError(GLFW_PLATFORM_ERROR,
                        "Wayland: Standard cursor \"%s\" not found",
                        translateCursorShape(shape));
        return GLFW_FALSE;
    }

Camilla Berglund's avatar
Cleanup    
Camilla Berglund committed
628
    cursor->wl.image = standardCursor->images[0];
629
    return GLFW_TRUE;
630
631
}

632
633
void _glfwPlatformDestroyCursor(_GLFWcursor* cursor)
{
634
635
636
637
    // If it's a standard cursor we don't need to do anything here
    if (cursor->wl.image)
        return;

638
639
    if (cursor->wl.buffer)
        wl_buffer_destroy(cursor->wl.buffer);
640
641
}

642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
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;

    _glfwInputCursorMotion(window,
                           wl_fixed_to_double(dxUnaccel),
                           wl_fixed_to_double(dyUnaccel));
}

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

739
740
void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor)
{
Jonas Ådahl's avatar
Jonas Ådahl committed
741
    struct wl_buffer* buffer;
742
    struct wl_cursor* defaultCursor;
Jonas Ådahl's avatar
Jonas Ådahl committed
743
744
    struct wl_cursor_image* image;
    struct wl_surface* surface = _glfw.wl.cursorSurface;
745
746
747
748
749
750
751
752
753
754
755

    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;

756
757
758
759
    // Unlock possible pointer lock if no longer disabled.
    if (window->cursorMode != GLFW_CURSOR_DISABLED && isPointerLocked(window))
        unlockPointer(window);

760
761
    if (window->cursorMode == GLFW_CURSOR_NORMAL)
    {
Camilla Berglund's avatar
Cleanup    
Camilla Berglund committed
762
763
764
        if (cursor)
            image = cursor->wl.image;
        else
765
        {
766
767
768
769
770
771
772
773
774
775
776
777
778
            defaultCursor = wl_cursor_theme_get_cursor(_glfw.wl.cursorTheme,
                                                       "left_ptr");
            if (!defaultCursor)
            {
                _glfwInputError(GLFW_PLATFORM_ERROR,
                                "Wayland: Standard cursor not found");
                return;
            }
            image = defaultCursor->images[0];
        }

        if (image)
        {
779
780
781
782
            buffer = wl_cursor_image_get_buffer(image);
            if (!buffer)
                return;
            wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.pointerSerial,
Jonas Ådahl's avatar
Jonas Ådahl committed
783
784
785
                                  surface,
                                  image->hotspot_x,
                                  image->hotspot_y);
786
787
            wl_surface_attach(surface, buffer, 0, 0);
            wl_surface_damage(surface, 0, 0,
Jonas Ådahl's avatar
Jonas Ådahl committed
788
                              image->width, image->height);
789
790
791
792
793
            wl_surface_commit(surface);
        }
        else
        {
            wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.pointerSerial,
Jonas Ådahl's avatar
Jonas Ådahl committed
794
795
796
                                  surface,
                                  cursor->wl.xhot,
                                  cursor->wl.yhot);
797
798
            wl_surface_attach(surface, cursor->wl.buffer, 0, 0);
            wl_surface_damage(surface, 0, 0,
Jonas Ådahl's avatar
Jonas Ådahl committed
799
                              cursor->wl.width, cursor->wl.height);
800
801
802
            wl_surface_commit(surface);
        }
    }
803
804
805
806
807
808
    else if (window->cursorMode == GLFW_CURSOR_DISABLED)
    {
        if (!isPointerLocked(window))
            lockPointer(window);
    }
    else if (window->cursorMode == GLFW_CURSOR_HIDDEN)
809
    {
810
811
        wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.pointerSerial,
                              NULL, 0, 0);
812
    }
813
814
}

815
816
817
818
819
820
821
822
823
824
825
826
827
void _glfwPlatformSetClipboardString(_GLFWwindow* window, const char* string)
{
    // TODO
    fprintf(stderr, "_glfwPlatformSetClipboardString not implemented yet\n");
}

const char* _glfwPlatformGetClipboardString(_GLFWwindow* window)
{
    // TODO
    fprintf(stderr, "_glfwPlatformGetClipboardString not implemented yet\n");
    return NULL;
}

828
char** _glfwPlatformGetRequiredInstanceExtensions(unsigned int* count)
Camilla Berglund's avatar
Camilla Berglund committed
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
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
{
    char** extensions;

    *count = 0;

    if (!_glfw.vk.KHR_wayland_surface)
        return NULL;

    extensions = calloc(2, sizeof(char*));
    extensions[0] = strdup("VK_KHR_surface");
    extensions[1] = strdup("VK_KHR_wayland_surface");

    *count = 2;
    return extensions;
}

int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance,
                                                      VkPhysicalDevice device,
                                                      unsigned int queuefamily)
{
    PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR vkGetPhysicalDeviceWaylandPresentationSupportKHR =
        (PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR)
        vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceWaylandPresentationSupportKHR");
    if (!vkGetPhysicalDeviceWaylandPresentationSupportKHR)
    {
        _glfwInputError(GLFW_API_UNAVAILABLE,
                        "Wayland: Vulkan instance missing VK_KHR_wayland_surface extension");
        return VK_NULL_HANDLE;
    }

    return vkGetPhysicalDeviceWaylandPresentationSupportKHR(device,
                                                            queuefamily,
                                                            _glfw.wl.display);
}

VkResult _glfwPlatformCreateWindowSurface(VkInstance instance,
                                          _GLFWwindow* window,
                                          const VkAllocationCallbacks* allocator,
                                          VkSurfaceKHR* surface)
{
    VkResult err;
    VkWaylandSurfaceCreateInfoKHR sci;
    PFN_vkCreateWaylandSurfaceKHR vkCreateWaylandSurfaceKHR;

    vkCreateWaylandSurfaceKHR = (PFN_vkCreateWaylandSurfaceKHR)
        vkGetInstanceProcAddr(instance, "vkCreateWaylandSurfaceKHR");
    if (!vkCreateWaylandSurfaceKHR)
    {
        _glfwInputError(GLFW_API_UNAVAILABLE,
                        "Wayland: Vulkan instance missing VK_KHR_wayland_surface extension");
        return VK_ERROR_EXTENSION_NOT_PRESENT;
    }

    memset(&sci, 0, sizeof(sci));
    sci.sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR;
    sci.display = _glfw.wl.display;
    sci.surface = window->wl.surface;

    err = vkCreateWaylandSurfaceKHR(instance, &sci, allocator, surface);
    if (err)
    {
        _glfwInputError(GLFW_PLATFORM_ERROR,
                        "Wayland: Failed to create Vulkan surface: %s",
                        _glfwGetVulkanResultString(err));
    }

    return err;
}

898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915

//////////////////////////////////////////////////////////////////////////
//////                        GLFW native API                       //////
//////////////////////////////////////////////////////////////////////////

GLFWAPI struct wl_display* glfwGetWaylandDisplay(void)
{
    _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
    return _glfw.wl.display;
}

GLFWAPI struct wl_surface* glfwGetWaylandWindow(GLFWwindow* handle)
{
    _GLFWwindow* window = (_GLFWwindow*) handle;
    _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
    return window->wl.surface;
}