wl_window.c 27 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
    if (window->monitor)
333
    {
Camilla Berglund's avatar
Camilla Berglund committed
334
335
336
337
        wl_shell_surface_set_fullscreen(
            window->wl.shell_surface,
            WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT,
            0,
338
            window->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
}

Camilla Berglund's avatar
Camilla Berglund committed
386
387
388
389
390
391
392
void _glfwPlatformSetWindowIcon(_GLFWwindow* window,
                                int count, const GLFWimage* images)
{
    // TODO
    fprintf(stderr, "_glfwPlatformSetWindowIcon not implemented yet\n");
}

393
394
void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos)
{
395
396
    // A Wayland client is not aware of its position, so just warn and leave it
    // as (0, 0)
397
398

    _glfwInputError(GLFW_PLATFORM_ERROR,
399
                    "Wayland: Window position retrieval not supported");
400
401
402
403
}

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

    _glfwInputError(GLFW_PLATFORM_ERROR,
407
                    "Wayland: Window position setting not supported");
408
409
410
411
412
}

void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height)
{
    if (width)
413
        *width = window->wl.width;
414
    if (height)
415
        *height = window->wl.height;
416
417
418
419
}

void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height)
{
420
421
    int scaledWidth = width * window->wl.scale;
    int scaledHeight = height * window->wl.scale;
422
423
    window->wl.width = width;
    window->wl.height = height;
424
425
    wl_egl_window_resize(window->wl.native, scaledWidth, scaledHeight, 0, 0);
    _glfwInputFramebufferSize(window, scaledWidth, scaledHeight);
426
427
}

428
429
430
431
432
433
434
435
436
437
438
439
440
441
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");
}

442
443
444
void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height)
{
    _glfwPlatformGetWindowSize(window, width, height);
445
446
    *width *= window->wl.scale;
    *height *= window->wl.scale;
447
448
}

449
450
451
452
453
454
455
456
void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window,
                                     int* left, int* top,
                                     int* right, int* bottom)
{
    // TODO
    fprintf(stderr, "_glfwPlatformGetWindowFrameSize not implemented yet\n");
}

457
458
459
460
461
462
463
464
465
466
467
468
void _glfwPlatformIconifyWindow(_GLFWwindow* window)
{
    // TODO
    fprintf(stderr, "_glfwPlatformIconifyWindow not implemented yet\n");
}

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

469
470
471
472
473
474
void _glfwPlatformMaximizeWindow(_GLFWwindow* window)
{
    // TODO
    fprintf(stderr, "_glfwPlatformMaximizeWindow not implemented yet\n");
}

475
476
void _glfwPlatformShowWindow(_GLFWwindow* window)
{
477
    wl_shell_surface_set_toplevel(window->wl.shell_surface);
478
479
480
481
}

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

Camilla Berglund's avatar
Camilla Berglund committed
486
487
488
489
490
491
void _glfwPlatformFocusWindow(_GLFWwindow* window)
{
    // TODO
    fprintf(stderr, "_glfwPlatformFocusWindow not implemented yet\n");
}

Camilla Berglund's avatar
Camilla Berglund committed
492
493
494
495
496
497
498
499
500
501
void _glfwPlatformSetWindowMonitor(_GLFWwindow* window,
                                   _GLFWmonitor* monitor,
                                   int xpos, int ypos,
                                   int width, int height,
                                   int refreshRate)
{
    // TODO
    fprintf(stderr, "_glfwPlatformSetWindowMonitor not implemented yet\n");
}

502
503
504
int _glfwPlatformWindowFocused(_GLFWwindow* window)
{
    // TODO
505
    return GLFW_FALSE;
506
507
508
509
510
}

int _glfwPlatformWindowIconified(_GLFWwindow* window)
{
    // TODO
511
    return GLFW_FALSE;
512
513
514
515
516
}

int _glfwPlatformWindowVisible(_GLFWwindow* window)
{
    // TODO
517
    return GLFW_FALSE;
518
519
}

520
521
522
523
524
525
int _glfwPlatformWindowMaximized(_GLFWwindow* window)
{
    // TODO
    return GLFW_FALSE;
}

526
527
void _glfwPlatformPollEvents(void)
{
528
    handleEvents(0);
529
530
531
532
}

void _glfwPlatformWaitEvents(void)
{
533
    handleEvents(-1);
534
535
}

Camilla Berglund's avatar
Camilla Berglund committed
536
537
538
539
540
void _glfwPlatformWaitEventsTimeout(double timeout)
{
    handleEvents((int) (timeout * 1e3));
}

541
542
void _glfwPlatformPostEmptyEvent(void)
{
543
    wl_display_sync(_glfw.wl.display);
544
545
}

546
547
void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos)
{
548
    if (xpos)
549
        *xpos = window->wl.cursorPosX;
550
    if (ypos)
551
        *ypos = window->wl.cursorPosY;
552
553
}

554
555
static GLFWbool isPointerLocked(_GLFWwindow* window);

556
557
void _glfwPlatformSetCursorPos(_GLFWwindow* window, double x, double y)
{
558
559
560
561
562
563
564
    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);
    }
565
566
}

567
void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode)
568
{
569
    _glfwPlatformSetCursor(window, window->wl.currentCursor);
570
}
Camilla Berglund's avatar
Camilla Berglund committed
571

Camilla Berglund's avatar
Camilla Berglund committed
572
573
574
575
576
577
const char* _glfwPlatformGetKeyName(int key, int scancode)
{
    // TODO
    return NULL;
}

578
579
580
581
int _glfwPlatformCreateCursor(_GLFWcursor* cursor,
                              const GLFWimage* image,
                              int xhot, int yhot)
{
Jonas Ådahl's avatar
Jonas Ådahl committed
582
    struct wl_shm_pool* pool;
583
584
    int stride = image->width * 4;
    int length = image->width * image->height * 4;
Jonas Ådahl's avatar
Jonas Ådahl committed
585
    void* data;
586
587
    int fd, i;

588
    fd = createAnonymousFile(length);
Jonas Ådahl's avatar
Jonas Ådahl committed
589
590
    if (fd < 0)
    {
591
        _glfwInputError(GLFW_PLATFORM_ERROR,
Jonas Ådahl's avatar
Jonas Ådahl committed
592
593
                        "Wayland: Creating a buffer file for %d B failed: %m\n",
                        length);
594
        return GLFW_FALSE;
595
596
597
    }

    data = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
Jonas Ådahl's avatar
Jonas Ådahl committed
598
599
    if (data == MAP_FAILED)
    {
600
        _glfwInputError(GLFW_PLATFORM_ERROR,
Jonas Ådahl's avatar
Jonas Ådahl committed
601
                        "Wayland: Cursor mmap failed: %m\n");
602
        close(fd);
603
        return GLFW_FALSE;
604
605
606
607
608
609
610
611
612
    }

    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)
    {
Camilla Berglund's avatar
Camilla Berglund committed
613
        unsigned int alpha = source[3];
614

Camilla Berglund's avatar
Camilla Berglund committed
615
616
617
618
        *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;
619
620
    }

Jonas Ådahl's avatar
Jonas Ådahl committed
621
622
623
624
625
    cursor->wl.buffer =
        wl_shm_pool_create_buffer(pool, 0,
                                  image->width,
                                  image->height,
                                  stride, WL_SHM_FORMAT_ARGB8888);
626
627
628
629
630
631
632
    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;
633
    return GLFW_TRUE;
634
635
}

636
637
int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape)
{
Camilla Berglund's avatar
Cleanup    
Camilla Berglund committed
638
    struct wl_cursor* standardCursor;
639

Camilla Berglund's avatar
Cleanup    
Camilla Berglund committed
640
641
642
643
    standardCursor = wl_cursor_theme_get_cursor(_glfw.wl.cursorTheme,
                                                translateCursorShape(shape));
    if (!standardCursor)
    {
644
645
646
647
648
649
        _glfwInputError(GLFW_PLATFORM_ERROR,
                        "Wayland: Standard cursor \"%s\" not found",
                        translateCursorShape(shape));
        return GLFW_FALSE;
    }

Camilla Berglund's avatar
Cleanup    
Camilla Berglund committed
650
    cursor->wl.image = standardCursor->images[0];
651
    return GLFW_TRUE;
652
653
}

654
655
void _glfwPlatformDestroyCursor(_GLFWcursor* cursor)
{
656
657
658
659
    // If it's a standard cursor we don't need to do anything here
    if (cursor->wl.image)
        return;

660
661
    if (cursor->wl.buffer)
        wl_buffer_destroy(cursor->wl.buffer);
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
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
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;
}

761
762
void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor)
{
Jonas Ådahl's avatar
Jonas Ådahl committed
763
    struct wl_buffer* buffer;
764
    struct wl_cursor* defaultCursor;
Jonas Ådahl's avatar
Jonas Ådahl committed
765
766
    struct wl_cursor_image* image;
    struct wl_surface* surface = _glfw.wl.cursorSurface;
767
768
769
770
771
772
773
774
775
776
777

    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;

778
779
780
781
    // Unlock possible pointer lock if no longer disabled.
    if (window->cursorMode != GLFW_CURSOR_DISABLED && isPointerLocked(window))
        unlockPointer(window);

782
783
    if (window->cursorMode == GLFW_CURSOR_NORMAL)
    {
Camilla Berglund's avatar
Cleanup    
Camilla Berglund committed
784
785
786
        if (cursor)
            image = cursor->wl.image;
        else
787
        {
788
789
790
791
792
793
794
795
796
797
798
799
800
            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)
        {
801
802
803
804
            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
805
806
807
                                  surface,
                                  image->hotspot_x,
                                  image->hotspot_y);
808
809
            wl_surface_attach(surface, buffer, 0, 0);
            wl_surface_damage(surface, 0, 0,
Jonas Ådahl's avatar
Jonas Ådahl committed
810
                              image->width, image->height);
811
812
813
814
815
            wl_surface_commit(surface);
        }
        else
        {
            wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.pointerSerial,
Jonas Ådahl's avatar
Jonas Ådahl committed
816
817
818
                                  surface,
                                  cursor->wl.xhot,
                                  cursor->wl.yhot);
819
820
            wl_surface_attach(surface, cursor->wl.buffer, 0, 0);
            wl_surface_damage(surface, 0, 0,
Jonas Ådahl's avatar
Jonas Ådahl committed
821
                              cursor->wl.width, cursor->wl.height);
822
823
824
            wl_surface_commit(surface);
        }
    }
825
826
827
828
829
830
    else if (window->cursorMode == GLFW_CURSOR_DISABLED)
    {
        if (!isPointerLocked(window))
            lockPointer(window);
    }
    else if (window->cursorMode == GLFW_CURSOR_HIDDEN)
831
    {
832
833
        wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.pointerSerial,
                              NULL, 0, 0);
834
    }
835
836
}

837
838
839
840
841
842
843
844
845
846
847
848
849
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;
}

850
char** _glfwPlatformGetRequiredInstanceExtensions(unsigned int* count)
Camilla Berglund's avatar
Camilla Berglund committed
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
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
{
    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;
}

920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937

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