SDL_WINDOW_ALLOW_HIGHDPI in Emscripten and iframe

I find the combination of using SDL_WINDOW_ALLOW_HIGHDPI when running a game that is in an iframe, breaks the fullscreen functionality - if your game goes in fullscreen, and then leaves, it can never again get into fullscreen. The problem is apparently that the canvas style width and height is set to a value bigger than the monitor resolution. Does someone else knows a workaround for this?

Overall I had not experimented much with the usecase of running a game in an iframe, and I am finding it quite challenging, lots of little issues, if you add in an Android device, where the screen can rotate, it’s been almost impossible to get things to work correctly. Even focus of the canvas in the iframe itself is a bit mysterious.

Running things in it’s own page, without an iframe though, things works as expected - SDL_WINDOW_ALLOW_HIGHDPI works fine in this case.

Also if anyone has ideas on how to debug/test stuff on an iframe, my local test didn’t match what I observed when uploading in itch.

1 Like

For the high-dpi flag causing the window to grow indefinitely, I think I found the problem

Emscripten_SetWindowSize sets the canvas width and height (not css width and height, actual width and height) multiplied by the pixel ratio. Instead it should just set the actual width and height, as the pixel ratio is handled elsewhere already.

Overall it feels it would be quite useful to be able to resize from JS the canvas and stuff in a way that SDL uses that size instead or some way of overriding SDL behavior because I don’t see it being able to cater to all use cases

  • Canvas uses all size of the html and can be Fullscreen
  • Canvas uses all size of the html and can be Fullscreen but the html is in an iframe of website with a different domain (most game websites)
  • Canvas in a fixed position inside html, and can also be Fullscreen (imagine an use case where there is like a dev environment in html with one “virtual” window being the game)

SDL code for handling canvas size feels overengineered, also it’s quite hard having the size aspects handled in non-JS, ideal behavior for me would be if SDL would always use whatever is set in the canvas as size, but never set a size to it on itself, this would make it much much easier - essentially treating the canvas like one monitor that has a fixed resolution, but the resolution can change on each frame.

1 Like

I ended up doing my own canvas resize events

void send_window_resize_event(int w, int h)
{
    SDL_Window *window = sys_get_window();
    if(window == nullptr) return;

    SDL_FlushEvent(SDL_WINDOWEVENT);

    Common::Debug::Printf(Common::MessageType::kDbgMsg_Info,"Sent Resize %d, %d", w, h);

    // SIZE_CHANGED is followed by RESIZED if the size was changed by an external event
    SDL_WindowEvent* windowEvent_sz = new SDL_WindowEvent;
    windowEvent_sz->windowID = SDL_GetWindowID(window);
    windowEvent_sz->type = SDL_WINDOWEVENT;
    windowEvent_sz->event = SDL_WINDOWEVENT_SIZE_CHANGED;
    windowEvent_sz->data1 = w;
    windowEvent_sz->data2 = h;
    SDL_PushEvent(reinterpret_cast<SDL_Event *>(windowEvent_sz));

    SDL_WindowEvent* windowEvent_rs = new SDL_WindowEvent;
    windowEvent_rs->windowID = SDL_GetWindowID(window);
    windowEvent_rs->type = SDL_WINDOWEVENT;
    windowEvent_rs->event = SDL_WINDOWEVENT_RESIZED;
    windowEvent_rs->data1 = w;
    windowEvent_rs->data2 = h;
    SDL_PushEvent(reinterpret_cast<SDL_Event *>(windowEvent_rs));
}

And then doing a size check per frame

double css_c_w = 0, css_c_h = 0;
  emscripten_get_element_css_size("canvas", &css_c_w, &css_c_h);
  int win_w = get_window_inner_width();
  int win_h = get_window_inner_height();

  if(WindowInnerSize.Width == win_w && WindowInnerSize.Height == win_h && win_w == (int) css_c_w && win_h == (int) css_c_h) return;
  WindowInnerSize.Width = win_w;
  WindowInnerSize.Height = win_h;

  send_window_resize_event(win_w, win_h);

  emscripten_set_canvas_element_size("canvas", win_w, win_h);
  emscripten_set_element_css_size("canvas", WindowInnerSize.Width, WindowInnerSize.Height);

This fixed for the use case of the canvas being in an page where it needs to take either all the area available or all the area available of an iframe where the canvas is inside of it. And also screen rotation and mobile stuff.

I can’t though enable high-dpi either using this or without using this as both will make the canvas size grow indefinitely on each resize.

But without the code above, when the canvas goes out of fullscreen, it’s set to the size of the fullscreen still, which makes that, if the iframe aspect ratio doesn’t match the screen aspect ratio, lots of black bars to appear and the game to be squished.

I would love to be able to push resize events that make SDL do the actual resize instead of the way that I just get notifications later but have to handle the resize, because in this way I could at least fix the broken high-dpi for myself.

I had similar problem with the following code

#include <SDL2/SDL.h>
#include <emscripten.h>
#include <stdio.h>

SDL_Window *window;
SDL_Renderer *renderer;

void draw() {
  int window_width;
  int window_height;
  SDL_GetWindowSize(window, &window_width, &window_height);

  int renderer_width;
  int renderer_height;
  SDL_GetRendererOutputSize(renderer, &renderer_width, &renderer_height);

  int x_multiplier = renderer_width / window_width;
  int y_multiplier = renderer_height / window_height;

  SDL_SetRenderDrawColor(renderer, 0, 0, 255, 255);
  SDL_RenderClear(renderer);

  SDL_SetRenderDrawColor(renderer, 255, 255, 0, 255);
  SDL_Rect r = {
      .x = 0, .y = 0, .h = 100 * y_multiplier, .w = 100 * x_multiplier};
  SDL_RenderFillRect(renderer, &r);
  SDL_RenderPresent(renderer);
}

int main(int argc, char *argv[]) {
  SDL_Init(SDL_INIT_VIDEO);

  window =
      SDL_CreateWindow(NULL, 0, 0, 200, 200,
                       SDL_WINDOW_SHOWN | SDL_WINDOW_OPENGL |
                           SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI);

  renderer = SDL_CreateRenderer(
      window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);

  emscripten_set_main_loop(draw, 0, 0);
  return 0;
}

When I switch between two monitors, the square will be shown weirdly.

But the problem goes away once I add SDL_WINDOW_RESIZABLE flag.

1 Like