wayland: HiDPI support
authorSebastian Krzyszkowiak <dos@dosowisko.net>
Wed, 12 Jun 2019 00:55:05 +0200
changeset 12844d92b5fbb5ac0
parent 12843 dcc0e295db57
child 12845 5b0bc0ed588a
wayland: HiDPI support
src/video/wayland/SDL_waylandopengles.c
src/video/wayland/SDL_waylandopengles.h
src/video/wayland/SDL_waylandvideo.c
src/video/wayland/SDL_waylandvideo.h
src/video/wayland/SDL_waylandvulkan.c
src/video/wayland/SDL_waylandvulkan.h
src/video/wayland/SDL_waylandwindow.c
src/video/wayland/SDL_waylandwindow.h
     1.1 --- a/src/video/wayland/SDL_waylandopengles.c	Wed Jun 12 13:56:20 2019 +0300
     1.2 +++ b/src/video/wayland/SDL_waylandopengles.c	Wed Jun 12 00:55:05 2019 +0200
     1.3 @@ -71,16 +71,24 @@
     1.4  
     1.5      // Wayland-EGL forbids drawing calls in-between SwapBuffers and wl_egl_window_resize
     1.6      if (data->resize.pending) {
     1.7 +        if (data->scale_factor != data->resize.scale_factor) {
     1.8 +            window->w = 0;
     1.9 +            window->h = 0;
    1.10 +        }
    1.11          SDL_SendWindowEvent(window, SDL_WINDOWEVENT_RESIZED, data->resize.width, data->resize.height);
    1.12          window->w = data->resize.width;
    1.13          window->h = data->resize.height;
    1.14 +        data->scale_factor = data->resize.scale_factor;
    1.15 +        wl_surface_set_buffer_scale(data->surface, data->scale_factor);
    1.16 +        WAYLAND_wl_egl_window_resize(data->egl_window, window->w * data->scale_factor, window->h * data->scale_factor, 0, 0);
    1.17  
    1.18 -        WAYLAND_wl_egl_window_resize(data->egl_window, window->w, window->h, 0, 0);
    1.19 -
    1.20 -        if (data->waylandData->shell.xdg) {
    1.21 -           xdg_surface_ack_configure(data->shell_surface.xdg.surface, data->resize.serial);
    1.22 -        } else if (data->waylandData->shell.zxdg) {
    1.23 -           zxdg_surface_v6_ack_configure(data->shell_surface.zxdg.surface, data->resize.serial);
    1.24 +        if (data->resize.configure) {
    1.25 +           if (data->waylandData->shell.xdg) {
    1.26 +              xdg_surface_ack_configure(data->shell_surface.xdg.surface, data->resize.serial);
    1.27 +           } else if (data->waylandData->shell.zxdg) {
    1.28 +              zxdg_surface_v6_ack_configure(data->shell_surface.zxdg.surface, data->resize.serial);
    1.29 +           }
    1.30 +           data->resize.configure = SDL_FALSE;
    1.31          }
    1.32  
    1.33          region = wl_compositor_create_region(data->waylandData->compositor);
    1.34 @@ -113,6 +121,23 @@
    1.35      return ret;
    1.36  }
    1.37  
    1.38 +void
    1.39 +Wayland_GLES_GetDrawableSize(_THIS, SDL_Window * window, int * w, int * h)
    1.40 +{
    1.41 +    SDL_WindowData *data;
    1.42 +    if (window->driverdata) {
    1.43 +        data = (SDL_WindowData *) window->driverdata;
    1.44 +
    1.45 +        if (w) {
    1.46 +            *w = window->w * data->scale_factor;
    1.47 +        }
    1.48 +
    1.49 +        if (h) {
    1.50 +            *h = window->h * data->scale_factor;
    1.51 +        }
    1.52 +    }
    1.53 +}
    1.54 +
    1.55  void 
    1.56  Wayland_GLES_DeleteContext(_THIS, SDL_GLContext context)
    1.57  {
     2.1 --- a/src/video/wayland/SDL_waylandopengles.h	Wed Jun 12 13:56:20 2019 +0300
     2.2 +++ b/src/video/wayland/SDL_waylandopengles.h	Wed Jun 12 00:55:05 2019 +0200
     2.3 @@ -42,6 +42,7 @@
     2.4  extern SDL_GLContext Wayland_GLES_CreateContext(_THIS, SDL_Window * window);
     2.5  extern int Wayland_GLES_SwapWindow(_THIS, SDL_Window * window);
     2.6  extern int Wayland_GLES_MakeCurrent(_THIS, SDL_Window * window, SDL_GLContext context);
     2.7 +extern void Wayland_GLES_GetDrawableSize(_THIS, SDL_Window * window, int * w, int * h);
     2.8  extern void Wayland_GLES_DeleteContext(_THIS, SDL_GLContext context);
     2.9  
    2.10  #endif /* SDL_waylandopengles_h_ */
     3.1 --- a/src/video/wayland/SDL_waylandvideo.c	Wed Jun 12 13:56:20 2019 +0300
     3.2 +++ b/src/video/wayland/SDL_waylandvideo.c	Wed Jun 12 00:55:05 2019 +0200
     3.3 @@ -172,6 +172,7 @@
     3.4      device->GL_SwapWindow = Wayland_GLES_SwapWindow;
     3.5      device->GL_GetSwapInterval = Wayland_GLES_GetSwapInterval;
     3.6      device->GL_SetSwapInterval = Wayland_GLES_SetSwapInterval;
     3.7 +    device->GL_GetDrawableSize = Wayland_GLES_GetDrawableSize;
     3.8      device->GL_MakeCurrent = Wayland_GLES_MakeCurrent;
     3.9      device->GL_CreateContext = Wayland_GLES_CreateContext;
    3.10      device->GL_LoadLibrary = Wayland_GLES_LoadLibrary;
    3.11 @@ -199,6 +200,7 @@
    3.12      device->Vulkan_UnloadLibrary = Wayland_Vulkan_UnloadLibrary;
    3.13      device->Vulkan_GetInstanceExtensions = Wayland_Vulkan_GetInstanceExtensions;
    3.14      device->Vulkan_CreateSurface = Wayland_Vulkan_CreateSurface;
    3.15 +    device->Vulkan_GetDrawableSize = Wayland_Vulkan_GetDrawableSize;
    3.16  #endif
    3.17  
    3.18      device->free = Wayland_DeleteDevice;
    3.19 @@ -226,7 +228,6 @@
    3.20      SDL_VideoDisplay *display = data;
    3.21  
    3.22      display->name = SDL_strdup(model);
    3.23 -    display->driverdata = output;
    3.24  }
    3.25  
    3.26  static void
    3.27 @@ -237,15 +238,15 @@
    3.28                      int height,
    3.29                      int refresh)
    3.30  {
    3.31 +    SDL_DisplayMode mode;
    3.32      SDL_VideoDisplay *display = data;
    3.33 -    SDL_DisplayMode mode;
    3.34  
    3.35      SDL_zero(mode);
    3.36      mode.format = SDL_PIXELFORMAT_RGB888;
    3.37      mode.w = width;
    3.38      mode.h = height;
    3.39      mode.refresh_rate = refresh / 1000; // mHz to Hz
    3.40 -    mode.driverdata = display->driverdata;
    3.41 +    mode.driverdata = ((SDL_WaylandOutputData*)display->driverdata)->output;
    3.42      SDL_AddDisplayMode(display, &mode);
    3.43  
    3.44      if (flags & WL_OUTPUT_MODE_CURRENT) {
    3.45 @@ -258,8 +259,10 @@
    3.46  display_handle_done(void *data,
    3.47                      struct wl_output *output)
    3.48  {
    3.49 +    /* !!! FIXME: this will fail on any further property changes! */
    3.50      SDL_VideoDisplay *display = data;
    3.51      SDL_AddVideoDisplay(display);
    3.52 +    wl_output_set_user_data(output, display->driverdata);
    3.53      SDL_free(display->name);
    3.54      SDL_free(display);
    3.55  }
    3.56 @@ -269,7 +272,8 @@
    3.57                       struct wl_output *output,
    3.58                       int32_t factor)
    3.59  {
    3.60 -    // TODO: do HiDPI stuff.
    3.61 +    SDL_VideoDisplay *display = data;
    3.62 +    ((SDL_WaylandOutputData*)display->driverdata)->scale_factor = factor;
    3.63  }
    3.64  
    3.65  static const struct wl_output_listener output_listener = {
    3.66 @@ -283,6 +287,7 @@
    3.67  Wayland_add_display(SDL_VideoData *d, uint32_t id)
    3.68  {
    3.69      struct wl_output *output;
    3.70 +    SDL_WaylandOutputData *data;
    3.71      SDL_VideoDisplay *display = SDL_malloc(sizeof *display);
    3.72      if (!display) {
    3.73          SDL_OutOfMemory();
    3.74 @@ -296,6 +301,10 @@
    3.75          SDL_free(display);
    3.76          return;
    3.77      }
    3.78 +    data = SDL_malloc(sizeof *data);
    3.79 +    data->output = output;
    3.80 +    data->scale_factor = 1.0;
    3.81 +    display->driverdata = data;
    3.82  
    3.83      wl_output_add_listener(output, &output_listener, display);
    3.84  }
    3.85 @@ -351,7 +360,7 @@
    3.86      /*printf("WAYLAND INTERFACE: %s\n", interface);*/
    3.87  
    3.88      if (strcmp(interface, "wl_compositor") == 0) {
    3.89 -        d->compositor = wl_registry_bind(d->registry, id, &wl_compositor_interface, 1);
    3.90 +        d->compositor = wl_registry_bind(d->registry, id, &wl_compositor_interface, SDL_min(3, version));
    3.91      } else if (strcmp(interface, "wl_output") == 0) {
    3.92          Wayland_add_display(d, id);
    3.93      } else if (strcmp(interface, "wl_seat") == 0) {
    3.94 @@ -466,7 +475,9 @@
    3.95  
    3.96      for (i = 0; i < _this->num_displays; ++i) {
    3.97          SDL_VideoDisplay *display = &_this->displays[i];
    3.98 -        wl_output_destroy(display->driverdata);
    3.99 +
   3.100 +        wl_output_destroy(((SDL_WaylandOutputData*)display->driverdata)->output);
   3.101 +        SDL_free(display->driverdata);
   3.102          display->driverdata = NULL;
   3.103  
   3.104          for (j = display->num_display_modes; j--;) {
     4.1 --- a/src/video/wayland/SDL_waylandvideo.h	Wed Jun 12 13:56:20 2019 +0300
     4.2 +++ b/src/video/wayland/SDL_waylandvideo.h	Wed Jun 12 00:55:05 2019 +0200
     4.3 @@ -82,6 +82,11 @@
     4.4      int relative_mouse_mode;
     4.5  } SDL_VideoData;
     4.6  
     4.7 +typedef struct {
     4.8 +    struct wl_output *output;
     4.9 +    float scale_factor;
    4.10 +} SDL_WaylandOutputData;
    4.11 +
    4.12  #endif /* SDL_waylandvideo_h_ */
    4.13  
    4.14  /* vi: set ts=4 sw=4 expandtab: */
     5.1 --- a/src/video/wayland/SDL_waylandvulkan.c	Wed Jun 12 13:56:20 2019 +0300
     5.2 +++ b/src/video/wayland/SDL_waylandvulkan.c	Wed Jun 12 00:55:05 2019 +0200
     5.3 @@ -127,6 +127,22 @@
     5.4              extensionsForWayland);
     5.5  }
     5.6  
     5.7 +void Wayland_Vulkan_GetDrawableSize(_THIS, SDL_Window *window, int *w, int *h)
     5.8 +{
     5.9 +    SDL_WindowData *data;
    5.10 +    if (window->driverdata) {
    5.11 +        data = (SDL_WindowData *) window->driverdata;
    5.12 +
    5.13 +        if (w) {
    5.14 +            *w = window->w * data->scale_factor;
    5.15 +        }
    5.16 +
    5.17 +        if (h) {
    5.18 +            *h = window->h * data->scale_factor;
    5.19 +        }
    5.20 +    }
    5.21 +}
    5.22 +
    5.23  SDL_bool Wayland_Vulkan_CreateSurface(_THIS,
    5.24                                    SDL_Window *window,
    5.25                                    VkInstance instance,
     6.1 --- a/src/video/wayland/SDL_waylandvulkan.h	Wed Jun 12 13:56:20 2019 +0300
     6.2 +++ b/src/video/wayland/SDL_waylandvulkan.h	Wed Jun 12 00:55:05 2019 +0200
     6.3 @@ -40,6 +40,7 @@
     6.4                                            SDL_Window *window,
     6.5                                            unsigned *count,
     6.6                                            const char **names);
     6.7 +void Wayland_Vulkan_GetDrawableSize(_THIS, SDL_Window *window, int *w, int *h);
     6.8  SDL_bool Wayland_Vulkan_CreateSurface(_THIS,
     6.9                                    SDL_Window *window,
    6.10                                    VkInstance instance,
     7.1 --- a/src/video/wayland/SDL_waylandwindow.c	Wed Jun 12 13:56:20 2019 +0300
     7.2 +++ b/src/video/wayland/SDL_waylandwindow.c	Wed Jun 12 00:55:05 2019 +0200
     7.3 @@ -38,6 +38,10 @@
     7.4  #include "xdg-decoration-unstable-v1-client-protocol.h"
     7.5  #include "org-kde-kwin-server-decoration-manager-client-protocol.h"
     7.6  
     7.7 +static float get_window_scale_factor(SDL_Window *window) {
     7.8 +      return ((SDL_WindowData*)window->driverdata)->scale_factor;
     7.9 +}
    7.10 +
    7.11  /* On modern desktops, we probably will use the xdg-shell protocol instead
    7.12     of wl_shell, but wl_shell might be useful on older Wayland installs that
    7.13     don't have the newer protocol, or embedded things that don't have a full
    7.14 @@ -107,11 +111,14 @@
    7.15      struct wl_region *region;
    7.16  
    7.17      if (!wind->shell_surface.zxdg.initial_configure_seen) {
    7.18 +        window->w = 0;
    7.19 +        window->h = 0;
    7.20          SDL_SendWindowEvent(window, SDL_WINDOWEVENT_RESIZED, wind->resize.width, wind->resize.height);
    7.21          window->w = wind->resize.width;
    7.22          window->h = wind->resize.height;
    7.23  
    7.24 -        WAYLAND_wl_egl_window_resize(wind->egl_window, window->w, window->h, 0, 0);
    7.25 +        wl_surface_set_buffer_scale(wind->surface, get_window_scale_factor(window));
    7.26 +        WAYLAND_wl_egl_window_resize(wind->egl_window, window->w * get_window_scale_factor(window), window->h * get_window_scale_factor(window), 0, 0);
    7.27  
    7.28          zxdg_surface_v6_ack_configure(zxdg, serial);
    7.29  
    7.30 @@ -123,6 +130,7 @@
    7.31          wind->shell_surface.zxdg.initial_configure_seen = SDL_TRUE;
    7.32      } else {
    7.33          wind->resize.pending = SDL_TRUE;
    7.34 +        wind->resize.configure = SDL_TRUE;
    7.35          wind->resize.serial = serial;
    7.36      }
    7.37  }
    7.38 @@ -208,11 +216,14 @@
    7.39      struct wl_region *region;
    7.40  
    7.41      if (!wind->shell_surface.xdg.initial_configure_seen) {
    7.42 +        window->w = 0;
    7.43 +        window->h = 0;
    7.44          SDL_SendWindowEvent(window, SDL_WINDOWEVENT_RESIZED, wind->resize.width, wind->resize.height);
    7.45          window->w = wind->resize.width;
    7.46          window->h = wind->resize.height;
    7.47  
    7.48 -        WAYLAND_wl_egl_window_resize(wind->egl_window, window->w, window->h, 0, 0);
    7.49 +        wl_surface_set_buffer_scale(wind->surface, get_window_scale_factor(window));
    7.50 +        WAYLAND_wl_egl_window_resize(wind->egl_window, window->w * get_window_scale_factor(window), window->h * get_window_scale_factor(window), 0, 0);
    7.51  
    7.52          xdg_surface_ack_configure(xdg, serial);
    7.53  
    7.54 @@ -224,6 +235,7 @@
    7.55          wind->shell_surface.xdg.initial_configure_seen = SDL_TRUE;
    7.56      } else {
    7.57          wind->resize.pending = SDL_TRUE;
    7.58 +        wind->resize.configure = SDL_TRUE;
    7.59          wind->resize.serial = serial;
    7.60      }
    7.61  }
    7.62 @@ -330,6 +342,78 @@
    7.63  };
    7.64  #endif /* SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH */
    7.65  
    7.66 +static void
    7.67 +update_scale_factor(SDL_WindowData *window) {
    7.68 +   float old_factor = window->scale_factor, new_factor = 0.0;
    7.69 +
    7.70 +   if (!(window->sdlwindow->flags & SDL_WINDOW_ALLOW_HIGHDPI)) {
    7.71 +       return;
    7.72 +   }
    7.73 +
    7.74 +   if (!window->num_outputs) {
    7.75 +       new_factor = old_factor;
    7.76 +   }
    7.77 +
    7.78 +   if (FULLSCREEN_VISIBLE(window->sdlwindow) && window->sdlwindow->fullscreen_mode.driverdata) {
    7.79 +       new_factor = ((SDL_WaylandOutputData*)(wl_output_get_user_data(window->sdlwindow->fullscreen_mode.driverdata)))->scale_factor;
    7.80 +   }
    7.81 +
    7.82 +   for (int i = 0; i < window->num_outputs; i++) {
    7.83 +       float factor = ((SDL_WaylandOutputData*)(wl_output_get_user_data(window->outputs[i])))->scale_factor;
    7.84 +       if (factor > new_factor) {
    7.85 +           new_factor = factor;
    7.86 +       }
    7.87 +   }
    7.88 +
    7.89 +   if (new_factor != old_factor) {
    7.90 +       /* force the resize event to trigger, as the logical size didn't change */
    7.91 +       window->resize.width = window->sdlwindow->w;
    7.92 +       window->resize.height = window->sdlwindow->h;
    7.93 +       window->resize.scale_factor = new_factor;
    7.94 +       window->resize.pending = SDL_TRUE;
    7.95 +   }
    7.96 +}
    7.97 +
    7.98 +static void
    7.99 +handle_surface_enter(void *data, struct wl_surface *surface,
   7.100 +        struct wl_output *output) {
   7.101 +    SDL_WindowData *window = data;
   7.102 +
   7.103 +    window->outputs = SDL_realloc(window->outputs, (window->num_outputs + 1) * sizeof *window->outputs);
   7.104 +    window->outputs[window->num_outputs++] = output;
   7.105 +    update_scale_factor(window);
   7.106 +}
   7.107 +
   7.108 +static void
   7.109 +handle_surface_leave(void *data, struct wl_surface *surface,
   7.110 +        struct wl_output *output) {
   7.111 +    SDL_WindowData *window = data;
   7.112 +
   7.113 +    if (window->num_outputs > 1) {
   7.114 +       struct wl_output **new_outputs = SDL_malloc((window->num_outputs - 1) * sizeof *window->outputs), **iter = new_outputs;
   7.115 +       for (int i=0; i < window->num_outputs; i++) {
   7.116 +           if (window->outputs[i] != output) {
   7.117 +               *iter = window->outputs[i];
   7.118 +               iter++;
   7.119 +           }
   7.120 +       }
   7.121 +       SDL_free(window->outputs);
   7.122 +       window->outputs = new_outputs;
   7.123 +       window->num_outputs--;
   7.124 +    } else {
   7.125 +       window->num_outputs = 0;
   7.126 +       SDL_free(window->outputs);
   7.127 +       window->outputs = NULL;
   7.128 +    }
   7.129 +
   7.130 +    update_scale_factor(window);
   7.131 +}
   7.132 +
   7.133 +static const struct wl_surface_listener surface_listener = {
   7.134 +    handle_surface_enter,
   7.135 +    handle_surface_leave
   7.136 +};
   7.137 +
   7.138  SDL_bool
   7.139  Wayland_GetWindowWMInfo(_THIS, SDL_Window * window, SDL_SysWMinfo * info)
   7.140  {
   7.141 @@ -475,7 +559,7 @@
   7.142  Wayland_SetWindowFullscreen(_THIS, SDL_Window * window,
   7.143                              SDL_VideoDisplay * _display, SDL_bool fullscreen)
   7.144  {
   7.145 -    struct wl_output *output = (struct wl_output *) _display->driverdata;
   7.146 +    struct wl_output *output = ((SDL_WaylandOutputData*) _display->driverdata)->output;
   7.147      SetFullscreen(_this, window, fullscreen ? output : NULL);
   7.148  }
   7.149  
   7.150 @@ -553,11 +637,28 @@
   7.151      data->waylandData = c;
   7.152      data->sdlwindow = window;
   7.153  
   7.154 +    data->scale_factor = 1.0;
   7.155 +
   7.156 +    if (window->flags & SDL_WINDOW_ALLOW_HIGHDPI) {
   7.157 +        for (int i=0; i < SDL_GetVideoDevice()->num_displays; i++) {
   7.158 +            float scale = ((SDL_WaylandOutputData*)SDL_GetVideoDevice()->displays[i].driverdata)->scale_factor;
   7.159 +            if (scale > data->scale_factor) {
   7.160 +                data->scale_factor = scale;
   7.161 +            }
   7.162 +        }
   7.163 +    }
   7.164 +
   7.165      data->resize.pending = SDL_FALSE;
   7.166 +    data->resize.width = window->w;
   7.167 +    data->resize.height = window->h;
   7.168 +    data->resize.scale_factor = data->scale_factor;
   7.169 +
   7.170 +    data->outputs = NULL;
   7.171 +    data->num_outputs = 0;
   7.172  
   7.173      data->surface =
   7.174          wl_compositor_create_surface(c->compositor);
   7.175 -    wl_surface_set_user_data(data->surface, data);
   7.176 +    wl_surface_add_listener(data->surface, &surface_listener, data);
   7.177  
   7.178      if (c->shell.xdg) {
   7.179          data->shell_surface.xdg.surface = xdg_wm_base_get_xdg_surface(c->shell.xdg, data->surface);
   7.180 @@ -587,7 +688,7 @@
   7.181  #endif /* SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH */
   7.182  
   7.183      data->egl_window = WAYLAND_wl_egl_window_create(data->surface,
   7.184 -                                            window->w, window->h);
   7.185 +                                            window->w * data->scale_factor, window->h * data->scale_factor);
   7.186  
   7.187      /* Create the GLES window surface */
   7.188      data->egl_surface = SDL_EGL_CreateSurface(_this, (NativeWindowType) data->egl_window);
   7.189 @@ -676,9 +777,10 @@
   7.190      SDL_WindowData *wind = window->driverdata;
   7.191      struct wl_region *region;
   7.192  
   7.193 -    WAYLAND_wl_egl_window_resize(wind->egl_window, window->w, window->h, 0, 0);
   7.194 +    wl_surface_set_buffer_scale(wind->surface, get_window_scale_factor(window));
   7.195 +    WAYLAND_wl_egl_window_resize(wind->egl_window, window->w * get_window_scale_factor(window), window->h * get_window_scale_factor(window), 0, 0);
   7.196  
   7.197 -    region =wl_compositor_create_region(data->compositor);
   7.198 +    region = wl_compositor_create_region(data->compositor);
   7.199      wl_region_add(region, 0, 0, window->w, window->h);
   7.200      wl_surface_set_opaque_region(wind->surface, region);
   7.201      wl_region_destroy(region);
     8.1 --- a/src/video/wayland/SDL_waylandwindow.h	Wed Jun 12 13:56:20 2019 +0300
     8.2 +++ b/src/video/wayland/SDL_waylandwindow.h	Wed Jun 12 00:55:05 2019 +0200
     8.3 @@ -71,11 +71,16 @@
     8.4  #endif /* SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH */
     8.5  
     8.6      struct {
     8.7 -        SDL_bool pending;
     8.8 +        SDL_bool pending, configure;
     8.9          uint32_t serial;
    8.10          int width, height;
    8.11 +        float scale_factor;
    8.12      } resize;
    8.13  
    8.14 +    struct wl_output **outputs;
    8.15 +    int num_outputs;
    8.16 +
    8.17 +    float scale_factor;
    8.18  } SDL_WindowData;
    8.19  
    8.20  extern void Wayland_ShowWindow(_THIS, SDL_Window *window);