Mac and Linux SDL2 binary snapshots
Edward Rudd
2018-08-19 561f0d614098a95527367cc3f911e476f35643d6
source/src/video/wayland/SDL_waylandevents.c
@@ -1,6 +1,6 @@
/*
  Simple DirectMedia Layer
  Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
  Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
  This software is provided 'as-is', without any express or implied
  warranty.  In no event will the authors be held liable for any damages
@@ -27,6 +27,7 @@
#include "SDL_assert.h"
#include "SDL_log.h"
#include "../../core/unix/SDL_poll.h"
#include "../../events/SDL_sysevents.h"
#include "../../events/SDL_events_c.h"
#include "../../events/scancodes_xfree86.h"
@@ -39,6 +40,7 @@
#include "pointer-constraints-unstable-v1-client-protocol.h"
#include "relative-pointer-unstable-v1-client-protocol.h"
#include "xdg-shell-unstable-v6-client-protocol.h"
#include <linux/input.h>
#include <sys/select.h>
@@ -51,7 +53,9 @@
    SDL_VideoData *display;
    struct wl_seat *seat;
    struct wl_pointer *pointer;
    struct wl_touch *touch;
    struct wl_keyboard *keyboard;
    SDL_WaylandDataDevice *data_device;
    struct zwp_relative_pointer_v1 *relative_pointer;
    SDL_WindowData *pointer_focus;
    SDL_WindowData *keyboard_focus;
@@ -69,20 +73,117 @@
    } xkb;
};
struct SDL_WaylandTouchPoint {
    SDL_TouchID id;
    float x;
    float y;
    struct wl_surface* surface;
    struct SDL_WaylandTouchPoint* prev;
    struct SDL_WaylandTouchPoint* next;
};
struct SDL_WaylandTouchPointList {
    struct SDL_WaylandTouchPoint* head;
    struct SDL_WaylandTouchPoint* tail;
};
static struct SDL_WaylandTouchPointList touch_points = {NULL, NULL};
static void
touch_add(SDL_TouchID id, float x, float y, struct wl_surface *surface)
{
    struct SDL_WaylandTouchPoint* tp = SDL_malloc(sizeof(struct SDL_WaylandTouchPoint));
    tp->id = id;
    tp->x = x;
    tp->y = y;
    tp->surface = surface;
    if (touch_points.tail) {
        touch_points.tail->next = tp;
        tp->prev = touch_points.tail;
    } else {
        touch_points.head = tp;
        tp->prev = NULL;
    }
    touch_points.tail = tp;
    tp->next = NULL;
}
static void
touch_update(SDL_TouchID id, float x, float y)
{
    struct SDL_WaylandTouchPoint* tp = touch_points.head;
    while (tp) {
        if (tp->id == id) {
            tp->x = x;
            tp->y = y;
        }
        tp = tp->next;
    }
}
static void
touch_del(SDL_TouchID id, float* x, float* y)
{
    struct SDL_WaylandTouchPoint* tp = touch_points.head;
    while (tp) {
        if (tp->id == id) {
            *x = tp->x;
            *y = tp->y;
            if (tp->prev) {
                tp->prev->next = tp->next;
            } else {
                touch_points.head = tp->next;
            }
            if (tp->next) {
                tp->next->prev = tp->prev;
            } else {
                touch_points.tail = tp->prev;
            }
            SDL_free(tp);
        }
        tp = tp->next;
    }
}
static struct wl_surface*
touch_surface(SDL_TouchID id)
{
    struct SDL_WaylandTouchPoint* tp = touch_points.head;
    while (tp) {
        if (tp->id == id) {
            return tp->surface;
        }
        tp = tp->next;
    }
    return NULL;
}
void
Wayland_PumpEvents(_THIS)
{
    SDL_VideoData *d = _this->driverdata;
    struct pollfd pfd[1];
    pfd[0].fd = WAYLAND_wl_display_get_fd(d->display);
    pfd[0].events = POLLIN;
    poll(pfd, 1, 0);
    if (pfd[0].revents & POLLIN)
    if (SDL_IOReady(WAYLAND_wl_display_get_fd(d->display), SDL_FALSE, 0)) {
        WAYLAND_wl_display_dispatch(d->display);
    }
    else
    {
        WAYLAND_wl_display_dispatch_pending(d->display);
    }
}
static void
@@ -97,7 +198,7 @@
        /* enter event for a window we've just destroyed */
        return;
    }
    /* This handler will be called twice in Wayland 1.4
     * Once for the window surface which has valid user data
     * and again for the mouse cursor surface which does not have valid user data
@@ -105,7 +206,7 @@
     */
    window = (SDL_WindowData *)wl_surface_get_user_data(surface);
    if (window) {
        input->pointer_focus = window;
        SDL_SetMouseFocus(window->sdlwindow);
@@ -148,15 +249,25 @@
    if (window->hit_test) {
        const SDL_Point point = { wl_fixed_to_int(input->sx_w), wl_fixed_to_int(input->sy_w) };
        const SDL_HitTestResult rc = window->hit_test(window, &point, window->hit_test_data);
        static const uint32_t directions[] = {
        static const uint32_t directions_wl[] = {
            WL_SHELL_SURFACE_RESIZE_TOP_LEFT, WL_SHELL_SURFACE_RESIZE_TOP,
            WL_SHELL_SURFACE_RESIZE_TOP_RIGHT, WL_SHELL_SURFACE_RESIZE_RIGHT,
            WL_SHELL_SURFACE_RESIZE_BOTTOM_RIGHT, WL_SHELL_SURFACE_RESIZE_BOTTOM,
            WL_SHELL_SURFACE_RESIZE_BOTTOM_LEFT, WL_SHELL_SURFACE_RESIZE_LEFT
        };
        /* the names are different (ZXDG_TOPLEVEL_V6_RESIZE_EDGE_* vs
           WL_SHELL_SURFACE_RESIZE_*), but the values are the same. */
        const uint32_t *directions_zxdg = directions_wl;
        switch (rc) {
            case SDL_HITTEST_DRAGGABLE:
                wl_shell_surface_move(window_data->shell_surface, input->seat, serial);
                if (input->display->shell.zxdg) {
                    zxdg_toplevel_v6_move(window_data->shell_surface.zxdg.roleobj.toplevel, input->seat, serial);
                } else {
                    wl_shell_surface_move(window_data->shell_surface.wl, input->seat, serial);
                }
                return SDL_TRUE;
            case SDL_HITTEST_RESIZE_TOPLEFT:
@@ -167,7 +278,11 @@
            case SDL_HITTEST_RESIZE_BOTTOM:
            case SDL_HITTEST_RESIZE_BOTTOMLEFT:
            case SDL_HITTEST_RESIZE_LEFT:
                wl_shell_surface_resize(window_data->shell_surface, input->seat, serial, directions[rc - SDL_HITTEST_RESIZE_TOPLEFT]);
                if (input->display->shell.zxdg) {
                    zxdg_toplevel_v6_resize(window_data->shell_surface.zxdg.roleobj.toplevel, input->seat, serial, directions_zxdg[rc - SDL_HITTEST_RESIZE_TOPLEFT]);
                } else {
                    wl_shell_surface_resize(window_data->shell_surface.wl, input->seat, serial, directions_wl[rc - SDL_HITTEST_RESIZE_TOPLEFT]);
                }
                return SDL_TRUE;
            default: return SDL_FALSE;
@@ -184,7 +299,7 @@
    SDL_WindowData *window = input->pointer_focus;
    enum wl_pointer_button_state state = state_w;
    uint32_t sdl_button;
    if  (input->pointer_focus) {
        switch (button) {
            case BTN_LEFT:
@@ -208,6 +323,8 @@
            default:
                return;
        }
        Wayland_data_device_set_serial(input->data_device, serial);
        SDL_SendMouseButton(window->sdlwindow, 0,
                            state ? SDL_PRESSED : SDL_RELEASED, sdl_button);
@@ -229,16 +346,16 @@
{
    SDL_WindowData *window = input->pointer_focus;
    enum wl_pointer_axis a = axis;
    int x, y;
    float x, y;
    if (input->pointer_focus) {
        switch (a) {
            case WL_POINTER_AXIS_VERTICAL_SCROLL:
                x = 0;
                y = wl_fixed_to_int(value);
                y = 0 - (float)wl_fixed_to_double(value);
                break;
            case WL_POINTER_AXIS_HORIZONTAL_SCROLL:
                x = wl_fixed_to_int(value);
                x = 0 - (float)wl_fixed_to_double(value);
                y = 0;
                break;
            default:
@@ -264,6 +381,75 @@
    pointer_handle_motion,
    pointer_handle_button,
    pointer_handle_axis,
    NULL, /* frame */
    NULL, /* axis_source */
    NULL, /* axis_stop */
    NULL, /* axis_discrete */
};
static void
touch_handler_down(void *data, struct wl_touch *touch, unsigned int serial,
                   unsigned int timestamp, struct wl_surface *surface,
                   int id, wl_fixed_t fx, wl_fixed_t fy)
{
    float x, y;
    SDL_WindowData* window;
    window = (SDL_WindowData *)wl_surface_get_user_data(surface);
    x = wl_fixed_to_double(fx) / window->sdlwindow->w;
    y = wl_fixed_to_double(fy) / window->sdlwindow->h;
    touch_add(id, x, y, surface);
    SDL_SendTouch(1, (SDL_FingerID)id, SDL_TRUE, x, y, 1.0f);
}
static void
touch_handler_up(void *data, struct wl_touch *touch, unsigned int serial,
                 unsigned int timestamp, int id)
{
    float x = 0, y = 0;
    touch_del(id, &x, &y);
    SDL_SendTouch(1, (SDL_FingerID)id, SDL_FALSE, x, y, 0.0f);
}
static void
touch_handler_motion(void *data, struct wl_touch *touch, unsigned int timestamp,
                     int id, wl_fixed_t fx, wl_fixed_t fy)
{
    float x, y;
    SDL_WindowData* window;
    window = (SDL_WindowData *)wl_surface_get_user_data(touch_surface(id));
    x = wl_fixed_to_double(fx) / window->sdlwindow->w;
    y = wl_fixed_to_double(fy) / window->sdlwindow->h;
    touch_update(id, x, y);
    SDL_SendTouchMotion(1, (SDL_FingerID)id, x, y, 1.0f);
}
static void
touch_handler_frame(void *data, struct wl_touch *touch)
{
}
static void
touch_handler_cancel(void *data, struct wl_touch *touch)
{
}
static const struct wl_touch_listener touch_listener = {
    touch_handler_down,
    touch_handler_up,
    touch_handler_motion,
    touch_handler_frame,
    touch_handler_cancel,
    NULL, /* shape */
    NULL, /* orientation */
};
static void
@@ -322,7 +508,7 @@
        /* enter event for a window we've just destroyed */
        return;
    }
    window = wl_surface_get_user_data(surface);
    if (window) {
@@ -373,6 +559,9 @@
        if (size > 0) {
            text[size] = 0;
            Wayland_data_device_set_serial(input->data_device, serial);
            SDL_SendKeyboardText(text);
        }
    }
@@ -396,6 +585,7 @@
    keyboard_handle_leave,
    keyboard_handle_key,
    keyboard_handle_modifiers,
    NULL, /* repeat_info */
};
static void
@@ -413,6 +603,19 @@
    } else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && input->pointer) {
        wl_pointer_destroy(input->pointer);
        input->pointer = NULL;
        input->display->pointer = NULL;
    }
    if ((caps & WL_SEAT_CAPABILITY_TOUCH) && !input->touch) {
        SDL_AddTouch(1, "wayland_touch");
        input->touch = wl_seat_get_touch(seat);
        wl_touch_set_user_data(input->touch, input);
        wl_touch_add_listener(input->touch, &touch_listener,
                                 input);
    } else if (!(caps & WL_SEAT_CAPABILITY_TOUCH) && input->touch) {
        SDL_DelTouch(1);
        wl_touch_destroy(input->touch);
        input->touch = NULL;
    }
    if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !input->keyboard) {
@@ -428,12 +631,248 @@
static const struct wl_seat_listener seat_listener = {
    seat_handle_capabilities,
    NULL, /* name */
};
static void
data_source_handle_target(void *data, struct wl_data_source *wl_data_source,
                          const char *mime_type)
{
}
static void
data_source_handle_send(void *data, struct wl_data_source *wl_data_source,
                        const char *mime_type, int32_t fd)
{
    Wayland_data_source_send((SDL_WaylandDataSource *)data, mime_type, fd);
}
static void
data_source_handle_cancelled(void *data, struct wl_data_source *wl_data_source)
{
    Wayland_data_source_destroy(data);
}
static void
data_source_handle_dnd_drop_performed(void *data, struct wl_data_source *wl_data_source)
{
}
static void
data_source_handle_dnd_finished(void *data, struct wl_data_source *wl_data_source)
{
}
static void
data_source_handle_action(void *data, struct wl_data_source *wl_data_source,
                          uint32_t dnd_action)
{
}
static const struct wl_data_source_listener data_source_listener = {
    data_source_handle_target,
    data_source_handle_send,
    data_source_handle_cancelled,
    data_source_handle_dnd_drop_performed, // Version 3
    data_source_handle_dnd_finished,       // Version 3
    data_source_handle_action,             // Version 3
};
SDL_WaylandDataSource*
Wayland_data_source_create(_THIS)
{
    SDL_WaylandDataSource *data_source = NULL;
    SDL_VideoData *driver_data = NULL;
    struct wl_data_source *id = NULL;
    if (_this == NULL || _this->driverdata == NULL) {
        SDL_SetError("Video driver uninitialized");
    } else {
        driver_data = _this->driverdata;
        if (driver_data->data_device_manager != NULL) {
            id = wl_data_device_manager_create_data_source(
                     driver_data->data_device_manager);
        }
        if (id == NULL) {
            SDL_SetError("Wayland unable to create data source");
        } else {
            data_source = SDL_calloc(1, sizeof *data_source);
            if (data_source == NULL) {
                SDL_OutOfMemory();
                wl_data_source_destroy(id);
            } else {
                WAYLAND_wl_list_init(&(data_source->mimes));
                data_source->source = id;
                wl_data_source_set_user_data(id, data_source);
                wl_data_source_add_listener(id, &data_source_listener,
                                            data_source);
            }
        }
    }
    return data_source;
}
static void
data_offer_handle_offer(void *data, struct wl_data_offer *wl_data_offer,
                        const char *mime_type)
{
    SDL_WaylandDataOffer *offer = data;
    Wayland_data_offer_add_mime(offer, mime_type);
}
static void
data_offer_handle_source_actions(void *data, struct wl_data_offer *wl_data_offer,
                                 uint32_t source_actions)
{
}
static void
data_offer_handle_actions(void *data, struct wl_data_offer *wl_data_offer,
                          uint32_t dnd_action)
{
}
static const struct wl_data_offer_listener data_offer_listener = {
    data_offer_handle_offer,
    data_offer_handle_source_actions, // Version 3
    data_offer_handle_actions,        // Version 3
};
static void
data_device_handle_data_offer(void *data, struct wl_data_device *wl_data_device,
                           struct wl_data_offer *id)
{
    SDL_WaylandDataOffer *data_offer = NULL;
    data_offer = SDL_calloc(1, sizeof *data_offer);
    if (data_offer == NULL) {
        SDL_OutOfMemory();
    } else {
        data_offer->offer = id;
        data_offer->data_device = data;
        WAYLAND_wl_list_init(&(data_offer->mimes));
        wl_data_offer_set_user_data(id, data_offer);
        wl_data_offer_add_listener(id, &data_offer_listener, data_offer);
    }
}
static void
data_device_handle_enter(void *data, struct wl_data_device *wl_data_device,
                       uint32_t serial, struct wl_surface *surface,
                         wl_fixed_t x, wl_fixed_t y, struct wl_data_offer *id)
{
    SDL_WaylandDataDevice *data_device = data;
    SDL_bool has_mime = SDL_FALSE;
    uint32_t dnd_action = WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE;
    data_device->drag_serial = serial;
    if (id != NULL) {
        data_device->drag_offer = wl_data_offer_get_user_data(id);
        /* TODO: SDL Support more mime types */
        has_mime = Wayland_data_offer_has_mime(
            data_device->drag_offer, FILE_MIME);
        /* If drag_mime is NULL this will decline the offer */
        wl_data_offer_accept(id, serial,
                             (has_mime == SDL_TRUE) ? FILE_MIME : NULL);
        /* SDL only supports "copy" style drag and drop */
        if (has_mime == SDL_TRUE) {
            dnd_action = WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY;
        }
        wl_data_offer_set_actions(data_device->drag_offer->offer,
                                  dnd_action, dnd_action);
    }
}
static void
data_device_handle_leave(void *data, struct wl_data_device *wl_data_device)
{
    SDL_WaylandDataDevice *data_device = data;
    SDL_WaylandDataOffer *offer = NULL;
    if (data_device->selection_offer != NULL) {
        data_device->selection_offer = NULL;
        Wayland_data_offer_destroy(offer);
    }
}
static void
data_device_handle_motion(void *data, struct wl_data_device *wl_data_device,
                        uint32_t time, wl_fixed_t x, wl_fixed_t y)
{
}
static void
data_device_handle_drop(void *data, struct wl_data_device *wl_data_device)
{
    SDL_WaylandDataDevice *data_device = data;
    void *buffer = NULL;
    size_t length = 0;
    const char *current_uri = NULL;
    const char *last_char = NULL;
    char *current_char = NULL;
    if (data_device->drag_offer != NULL) {
        /* TODO: SDL Support more mime types */
        buffer = Wayland_data_offer_receive(data_device->drag_offer,
                                            &length, FILE_MIME, SDL_FALSE);
        /* uri-list */
        current_uri = (const char *)buffer;
        last_char = (const char *)buffer + length;
        for (current_char = buffer; current_char < last_char; ++current_char) {
            if (*current_char == '\n' || *current_char == 0) {
                if (*current_uri != 0 && *current_uri != '#') {
                    *current_char = 0;
                    SDL_SendDropFile(NULL, current_uri);
                }
                current_uri = (const char *)current_char + 1;
            }
        }
        SDL_free(buffer);
    }
}
static void
data_device_handle_selection(void *data, struct wl_data_device *wl_data_device,
                          struct wl_data_offer *id)
{
    SDL_WaylandDataDevice *data_device = data;
    SDL_WaylandDataOffer *offer = NULL;
    if (id != NULL) {
        offer = wl_data_offer_get_user_data(id);
    }
    if (data_device->selection_offer != offer) {
        Wayland_data_offer_destroy(data_device->selection_offer);
        data_device->selection_offer = offer;
    }
    SDL_SendClipboardUpdate();
}
static const struct wl_data_device_listener data_device_listener = {
    data_device_handle_data_offer,
    data_device_handle_enter,
    data_device_handle_leave,
    data_device_handle_motion,
    data_device_handle_drop,
    data_device_handle_selection
};
void
Wayland_display_add_input(SDL_VideoData *d, uint32_t id)
{
    struct SDL_WaylandInput *input;
    SDL_WaylandDataDevice *data_device = NULL;
    input = SDL_calloc(1, sizeof *input);
    if (input == NULL)
@@ -444,6 +883,27 @@
    input->sx_w = wl_fixed_from_int(0);
    input->sy_w = wl_fixed_from_int(0);
    d->input = input;
    if (d->data_device_manager != NULL) {
        data_device = SDL_calloc(1, sizeof *data_device);
        if (data_device == NULL) {
            return;
        }
        data_device->data_device = wl_data_device_manager_get_data_device(
            d->data_device_manager, input->seat
        );
        data_device->video_data = d;
        if (data_device->data_device == NULL) {
            SDL_free(data_device);
        } else {
            wl_data_device_set_user_data(data_device->data_device, data_device);
            wl_data_device_add_listener(data_device->data_device,
                                        &data_device_listener, data_device);
            input->data_device = data_device;
        }
    }
    wl_seat_add_listener(input->seat, &seat_listener, input);
    wl_seat_set_user_data(input->seat, input);
@@ -458,11 +918,30 @@
    if (!input)
        return;
    if (input->data_device != NULL) {
        Wayland_data_device_clear_selection(input->data_device);
        if (input->data_device->selection_offer != NULL) {
            Wayland_data_offer_destroy(input->data_device->selection_offer);
        }
        if (input->data_device->drag_offer != NULL) {
            Wayland_data_offer_destroy(input->data_device->drag_offer);
        }
        if (input->data_device->data_device != NULL) {
            wl_data_device_release(input->data_device->data_device);
        }
        SDL_free(input->data_device);
    }
    if (input->keyboard)
        wl_keyboard_destroy(input->keyboard);
    if (input->pointer)
        wl_pointer_destroy(input->pointer);
    if (input->touch) {
        SDL_DelTouch(1);
        wl_touch_destroy(input->touch);
    }
    if (input->seat)
        wl_seat_destroy(input->seat);
@@ -477,6 +956,16 @@
    d->input = NULL;
}
SDL_WaylandDataDevice* Wayland_get_data_device(struct SDL_WaylandInput *input)
{
    if (input == NULL) {
        return NULL;
    }
    return input->data_device;
}
/* !!! FIXME: just merge these into display_handle_global(). */
void Wayland_display_add_relative_pointer_manager(SDL_VideoData *d, uint32_t id)
{
    d->relative_pointer_manager =