wl_window.c 28 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
    _GLFWwindow* window = data;
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
    float aspectRatio;
    float targetRatio;

    if (!window->monitor)
    {
        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;
    }

84
85
86
    _glfwInputWindowSize(window, width, height);
    _glfwPlatformSetWindowSize(window, width, height);
    _glfwInputWindowDamage(window);
87
88
}

Camilla Berglund's avatar
Camilla Berglund committed
89
90
static void handlePopupDone(void* data,
                            struct wl_shell_surface* shellSurface)
91
92
93
94
95
96
97
98
99
{
}

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

100
101
102
103
104
105
106
static void checkScaleChange(_GLFWwindow* window)
{
    int scaledWidth, scaledHeight;
    int scale = 1;
    int i;
    int monitorScale;

107
    // Check if we will be able to set the buffer scale or not.
108
    if (_glfw.wl.wl_compositor_version < 3)
109
110
        return;

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

177
178
static GLFWbool createSurface(_GLFWwindow* window,
                              const _GLFWwndconfig* wndconfig)
179
{
180
181
    window->wl.surface = wl_compositor_create_surface(_glfw.wl.compositor);
    if (!window->wl.surface)
182
        return GLFW_FALSE;
183

184
185
186
187
    wl_surface_add_listener(window->wl.surface,
                            &surfaceListener,
                            window);

188
189
    wl_surface_set_user_data(window->wl.surface, window);

190
191
192
193
    window->wl.native = wl_egl_window_create(window->wl.surface,
                                             wndconfig->width,
                                             wndconfig->height);
    if (!window->wl.native)
194
        return GLFW_FALSE;
195

196
197
198
    window->wl.shell_surface = wl_shell_get_shell_surface(_glfw.wl.shell,
                                                          window->wl.surface);
    if (!window->wl.shell_surface)
199
        return GLFW_FALSE;
200

201
    wl_shell_surface_add_listener(window->wl.shell_surface,
202
203
204
                                  &shellSurfaceListener,
                                  window);

205
206
    window->wl.width = wndconfig->width;
    window->wl.height = wndconfig->height;
207
    window->wl.scale = 1;
208

209
    return GLFW_TRUE;
210
211
}

212
static int
213
createTmpfileCloexec(char* tmpname)
214
215
216
217
218
219
220
221
222
223
{
    int fd;

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

    return fd;
}

224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
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);
    }
}

261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
/*
 * 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.
 *
276
277
278
279
 * 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.
280
281
 */
int
282
createAnonymousFile(off_t size)
283
284
{
    static const char template[] = "/glfw-shared-XXXXXX";
285
286
    const char* path;
    char* name;
287
288
289
290
    int fd;
    int ret;

    path = getenv("XDG_RUNTIME_DIR");
291
292
    if (!path)
    {
293
294
295
296
        errno = ENOENT;
        return -1;
    }

297
    name = calloc(strlen(path) + sizeof(template), 1);
298
299
300
    strcpy(name, path);
    strcat(name, template);

301
    fd = createTmpfileCloexec(name);
302
303
304
305
306
307

    free(name);

    if (fd < 0)
        return -1;
    ret = posix_fallocate(fd, 0, size);
308
309
    if (ret != 0)
    {
310
311
312
313
314
315
        close(fd);
        errno = ret;
        return -1;
    }
    return fd;
}
316

317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
// 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;
}

339
340
341
342
343
344
345
346
347
//////////////////////////////////////////////////////////////////////////
//////                       GLFW platform API                      //////
//////////////////////////////////////////////////////////////////////////

int _glfwPlatformCreateWindow(_GLFWwindow* window,
                              const _GLFWwndconfig* wndconfig,
                              const _GLFWctxconfig* ctxconfig,
                              const _GLFWfbconfig* fbconfig)
{
348
349
350
    if (!createSurface(window, wndconfig))
        return GLFW_FALSE;

351
352
    if (ctxconfig->api != GLFW_NO_API)
    {
353
        if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig))
354
355
            return GLFW_FALSE;
    }
356

357
    if (window->monitor)
358
    {
Camilla Berglund's avatar
Camilla Berglund committed
359
360
361
362
        wl_shell_surface_set_fullscreen(
            window->wl.shell_surface,
            WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT,
            0,
363
            window->monitor->wl.output);
364
365
366
    }
    else
    {
367
        wl_shell_surface_set_toplevel(window->wl.shell_surface);
368
369
    }

370
371
    window->wl.currentCursor = NULL;

372
373
374
375
    window->wl.monitors = calloc(1, sizeof(_GLFWmonitor*));
    window->wl.monitorsCount = 0;
    window->wl.monitorsSize = 1;

376
    return GLFW_TRUE;
377
378
379
380
}

void _glfwPlatformDestroyWindow(_GLFWwindow* window)
{
381
382
383
    if (window == _glfw.wl.pointerFocus)
    {
        _glfw.wl.pointerFocus = NULL;
384
        _glfwInputCursorEnter(window, GLFW_FALSE);
385
386
387
388
    }
    if (window == _glfw.wl.keyboardFocus)
    {
        _glfw.wl.keyboardFocus = NULL;
389
        _glfwInputWindowFocus(window, GLFW_FALSE);
390
391
    }

392
    _glfwDestroyContextEGL(window);
393

394
395
    if (window->wl.native)
        wl_egl_window_destroy(window->wl.native);
396

397
398
    if (window->wl.shell_surface)
        wl_shell_surface_destroy(window->wl.shell_surface);
399

400
401
    if (window->wl.surface)
        wl_surface_destroy(window->wl.surface);
402
403

    free(window->wl.monitors);
404
405
406
407
}

void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title)
{
408
    wl_shell_surface_set_title(window->wl.shell_surface, title);
409
410
}

Camilla Berglund's avatar
Camilla Berglund committed
411
412
413
414
415
416
417
void _glfwPlatformSetWindowIcon(_GLFWwindow* window,
                                int count, const GLFWimage* images)
{
    // TODO
    fprintf(stderr, "_glfwPlatformSetWindowIcon not implemented yet\n");
}

418
419
void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos)
{
420
421
    // A Wayland client is not aware of its position, so just warn and leave it
    // as (0, 0)
422
423

    _glfwInputError(GLFW_PLATFORM_ERROR,
424
                    "Wayland: Window position retrieval not supported");
425
426
427
428
}

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

    _glfwInputError(GLFW_PLATFORM_ERROR,
432
                    "Wayland: Window position setting not supported");
433
434
435
436
437
}

void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height)
{
    if (width)
438
        *width = window->wl.width;
439
    if (height)
440
        *height = window->wl.height;
441
442
443
444
}

void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height)
{
445
446
    int scaledWidth = width * window->wl.scale;
    int scaledHeight = height * window->wl.scale;
447
448
    window->wl.width = width;
    window->wl.height = height;
449
450
    wl_egl_window_resize(window->wl.native, scaledWidth, scaledHeight, 0, 0);
    _glfwInputFramebufferSize(window, scaledWidth, scaledHeight);
451
452
}

453
454
455
456
void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window,
                                      int minwidth, int minheight,
                                      int maxwidth, int maxheight)
{
457
458
    // TODO: find out how to trigger a resize.
    // The actual limits are checked in the wl_shell_surface::configure handler.
459
460
461
462
}

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

467
468
469
void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height)
{
    _glfwPlatformGetWindowSize(window, width, height);
470
471
    *width *= window->wl.scale;
    *height *= window->wl.scale;
472
473
}

474
475
476
477
478
479
480
481
void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window,
                                     int* left, int* top,
                                     int* right, int* bottom)
{
    // TODO
    fprintf(stderr, "_glfwPlatformGetWindowFrameSize not implemented yet\n");
}

482
483
484
485
486
487
488
489
490
491
492
493
void _glfwPlatformIconifyWindow(_GLFWwindow* window)
{
    // TODO
    fprintf(stderr, "_glfwPlatformIconifyWindow not implemented yet\n");
}

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

494
495
496
497
498
499
void _glfwPlatformMaximizeWindow(_GLFWwindow* window)
{
    // TODO
    fprintf(stderr, "_glfwPlatformMaximizeWindow not implemented yet\n");
}

500
501
void _glfwPlatformShowWindow(_GLFWwindow* window)
{
502
    wl_shell_surface_set_toplevel(window->wl.shell_surface);
503
504
505
506
}

void _glfwPlatformHideWindow(_GLFWwindow* window)
{
507
508
    wl_surface_attach(window->wl.surface, NULL, 0, 0);
    wl_surface_commit(window->wl.surface);
509
510
}

Camilla Berglund's avatar
Camilla Berglund committed
511
512
513
514
515
516
void _glfwPlatformFocusWindow(_GLFWwindow* window)
{
    // TODO
    fprintf(stderr, "_glfwPlatformFocusWindow not implemented yet\n");
}

Camilla Berglund's avatar
Camilla Berglund committed
517
518
519
520
521
522
523
524
525
526
void _glfwPlatformSetWindowMonitor(_GLFWwindow* window,
                                   _GLFWmonitor* monitor,
                                   int xpos, int ypos,
                                   int width, int height,
                                   int refreshRate)
{
    // TODO
    fprintf(stderr, "_glfwPlatformSetWindowMonitor not implemented yet\n");
}

527
528
529
int _glfwPlatformWindowFocused(_GLFWwindow* window)
{
    // TODO
530
    return GLFW_FALSE;
531
532
533
534
535
}

int _glfwPlatformWindowIconified(_GLFWwindow* window)
{
    // TODO
536
    return GLFW_FALSE;
537
538
539
540
541
}

int _glfwPlatformWindowVisible(_GLFWwindow* window)
{
    // TODO
542
    return GLFW_FALSE;
543
544
}

545
546
547
548
549
550
int _glfwPlatformWindowMaximized(_GLFWwindow* window)
{
    // TODO
    return GLFW_FALSE;
}

551
552
void _glfwPlatformPollEvents(void)
{
553
    handleEvents(0);
554
555
556
557
}

void _glfwPlatformWaitEvents(void)
{
558
    handleEvents(-1);
559
560
}

Camilla Berglund's avatar
Camilla Berglund committed
561
562
563
564
565
void _glfwPlatformWaitEventsTimeout(double timeout)
{
    handleEvents((int) (timeout * 1e3));
}

566
567
void _glfwPlatformPostEmptyEvent(void)
{
568
    wl_display_sync(_glfw.wl.display);
569
570
}

571
572
void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos)
{
573
    if (xpos)
574
        *xpos = window->wl.cursorPosX;
575
    if (ypos)
576
        *ypos = window->wl.cursorPosY;
577
578
}

579
580
static GLFWbool isPointerLocked(_GLFWwindow* window);

581
582
void _glfwPlatformSetCursorPos(_GLFWwindow* window, double x, double y)
{
583
584
585
586
587
588
589
    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);
    }
590
591
}

592
void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode)
593
{
594
    _glfwPlatformSetCursor(window, window->wl.currentCursor);
595
}
Camilla Berglund's avatar
Camilla Berglund committed
596

Camilla Berglund's avatar
Camilla Berglund committed
597
598
599
600
601
602
const char* _glfwPlatformGetKeyName(int key, int scancode)
{
    // TODO
    return NULL;
}

603
604
605
606
int _glfwPlatformCreateCursor(_GLFWcursor* cursor,
                              const GLFWimage* image,
                              int xhot, int yhot)
{
Jonas Ådahl's avatar
Jonas Ådahl committed
607
    struct wl_shm_pool* pool;
608
609
    int stride = image->width * 4;
    int length = image->width * image->height * 4;
Jonas Ådahl's avatar
Jonas Ådahl committed
610
    void* data;
611
612
    int fd, i;

613
    fd = createAnonymousFile(length);
Jonas Ådahl's avatar
Jonas Ådahl committed
614
615
    if (fd < 0)
    {
616
        _glfwInputError(GLFW_PLATFORM_ERROR,
Jonas Ådahl's avatar
Jonas Ådahl committed
617
618
                        "Wayland: Creating a buffer file for %d B failed: %m\n",
                        length);
619
        return GLFW_FALSE;
620
621
622
    }

    data = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
Jonas Ådahl's avatar
Jonas Ådahl committed
623
624
    if (data == MAP_FAILED)
    {
625
        _glfwInputError(GLFW_PLATFORM_ERROR,
Jonas Ådahl's avatar
Jonas Ådahl committed
626
                        "Wayland: Cursor mmap failed: %m\n");
627
        close(fd);
628
        return GLFW_FALSE;
629
630
631
632
633
634
635
636
637
    }

    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
638
        unsigned int alpha = source[3];
639

Camilla Berglund's avatar
Camilla Berglund committed
640
641
642
643
        *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;
644
645
    }

Jonas Ådahl's avatar
Jonas Ådahl committed
646
647
648
649
650
    cursor->wl.buffer =
        wl_shm_pool_create_buffer(pool, 0,
                                  image->width,
                                  image->height,
                                  stride, WL_SHM_FORMAT_ARGB8888);
651
652
653
654
655
656
657
    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;
658
    return GLFW_TRUE;
659
660
}

661
662
int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape)
{
Camilla Berglund's avatar
Cleanup    
Camilla Berglund committed
663
    struct wl_cursor* standardCursor;
664

Camilla Berglund's avatar
Cleanup    
Camilla Berglund committed
665
666
667
668
    standardCursor = wl_cursor_theme_get_cursor(_glfw.wl.cursorTheme,
                                                translateCursorShape(shape));
    if (!standardCursor)
    {
669
670
671
672
673
674
        _glfwInputError(GLFW_PLATFORM_ERROR,
                        "Wayland: Standard cursor \"%s\" not found",
                        translateCursorShape(shape));
        return GLFW_FALSE;
    }

Camilla Berglund's avatar
Cleanup    
Camilla Berglund committed
675
    cursor->wl.image = standardCursor->images[0];
676
    return GLFW_TRUE;
677
678
}

679
680
void _glfwPlatformDestroyCursor(_GLFWcursor* cursor)
{
681
682
683
684
    // If it's a standard cursor we don't need to do anything here
    if (cursor->wl.image)
        return;

685
686
    if (cursor->wl.buffer)
        wl_buffer_destroy(cursor->wl.buffer);
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
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
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;
}

786
787
void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor)
{
Jonas Ådahl's avatar
Jonas Ådahl committed
788
    struct wl_buffer* buffer;
789
    struct wl_cursor* defaultCursor;
Jonas Ådahl's avatar
Jonas Ådahl committed
790
791
    struct wl_cursor_image* image;
    struct wl_surface* surface = _glfw.wl.cursorSurface;
792
793
794
795
796
797
798
799
800
801
802

    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;

803
804
805
806
    // Unlock possible pointer lock if no longer disabled.
    if (window->cursorMode != GLFW_CURSOR_DISABLED && isPointerLocked(window))
        unlockPointer(window);

807
808
    if (window->cursorMode == GLFW_CURSOR_NORMAL)
    {
Camilla Berglund's avatar
Cleanup    
Camilla Berglund committed
809
810
811
        if (cursor)
            image = cursor->wl.image;
        else
812
        {
813
814
815
816
817
818
819
820
821
822
823
824
825
            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)
        {
826
827
828
829
            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
830
831
832
                                  surface,
                                  image->hotspot_x,
                                  image->hotspot_y);
833
834
            wl_surface_attach(surface, buffer, 0, 0);
            wl_surface_damage(surface, 0, 0,
Jonas Ådahl's avatar
Jonas Ådahl committed
835
                              image->width, image->height);
836
837
838
839
840
            wl_surface_commit(surface);
        }
        else
        {
            wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.pointerSerial,
Jonas Ådahl's avatar
Jonas Ådahl committed
841
842
843
                                  surface,
                                  cursor->wl.xhot,
                                  cursor->wl.yhot);
844
845
            wl_surface_attach(surface, cursor->wl.buffer, 0, 0);
            wl_surface_damage(surface, 0, 0,
Jonas Ådahl's avatar
Jonas Ådahl committed
846
                              cursor->wl.width, cursor->wl.height);
847
848
849
            wl_surface_commit(surface);
        }
    }
850
851
852
853
854
855
    else if (window->cursorMode == GLFW_CURSOR_DISABLED)
    {
        if (!isPointerLocked(window))
            lockPointer(window);
    }
    else if (window->cursorMode == GLFW_CURSOR_HIDDEN)
856
    {
857
858
        wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.pointerSerial,
                              NULL, 0, 0);
859
    }
860
861
}

862
863
864
865
866
867
868
869
870
871
872
873
874
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;
}

875
char** _glfwPlatformGetRequiredInstanceExtensions(uint32_t* count)
Camilla Berglund's avatar
Camilla Berglund committed
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
{
    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,
894
                                                      uint32_t queuefamily)
Camilla Berglund's avatar
Camilla Berglund committed
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
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
{
    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;
}

945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962

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