glViewport not working with maximized window

For my application, I want to maintain a square viewport when the window gets resized. I have defined a windowSizeCallback function to redefine the viewport each time the window is resized; see attached example. This works fine when the window is resized when I drag the corner, but it doesn’t work when the window is maximized, even though the callback function does get called when the window is maximized. What am I doing wrong? Or is this a bug in GLFW? I think it might be GLFW, because a similar GLUT program does work as expected. Thanks in advance for any reply!

GLFW version: 3.3.8 Cocoa NSGL EGL OSMesa dynamic
MacOS 10.14.6
GLFW Python bindings version 2.5.5
Python 3.10

import glfw
from OpenGL.GL import *

# Make sure we always have a square viewport
def window_size_callback(window, x, y):
    if x > y:
        glViewport(0, 0, y, y)
    else:
        glViewport(0, y - x, x, x)

print(f"GLFW version: {glfw.get_version_string().decode()}")

# Initialize the GLFW library
if not glfw.init():
    exit()

# Create a windowed mode window and its OpenGL context
window = glfw.create_window(200, 200, "Test Viewport", None, None)
if not window:
    glfw.terminate()
    exit()

# Make the window's context current
glfw.make_context_current(window)

# Set the callback function for resizing the window
glfw.set_window_size_callback(window, window_size_callback)

while not glfw.window_should_close(window):
    glClear(GL_COLOR_BUFFER_BIT)
    glBegin(GL_LINES)
    glVertex(-1, -1)
    glVertex(1, 1)
    glVertex(-1, 1)
    glVertex(1, -1)
    glEnd()

    # Swap front and back buffers
    glfw.swap_buffers(window)

    # Wait for events
    glfw.wait_events()

glfw.terminate()

The window/screen coordinates on MacOS are not in pixels. You need to use glfwGetFramebufferSize. You can set a callback for the frame buffer size changed events too.

Hi ws909,

Thanks for your reply. I am not sure I understand what you mean, though. Both glfwGetWindowSize and glfwGetFramebufferSize report the same numbers as the arguments supplied to the window size callback function. Using the framebuffer size callback instead of the window size callback has the same effect. As said above, the example code works fine when dragging the window corner, but fails when the window is maximized, even though the callback function does get called. Can you check whether you see the same behaviour on your setup? Thanks!

Best regards,

Wouter

@wbt

That might be, but glViewport and other OpenGL functions that don’t use normalized coordinates operate on frame buffer coordinates, which are guaranteed to be different from window or screen coordinates, on certain platforms. MacOS is one of these. This is the case on my current MacBook (let’s exclude that bug I reported a year or two ago which none of us have found a fix for yet), but if I remember correctly, an older MBA with a 1440x900 screen that I had, used a 1:1 scaling, resulting in similar sizes for the frame buffer and window size.

Short answer: you’re doing it incorrectly, and you need to change to glfwSetFramebufferCallback and glfwGetFramebufferSize.

Not that this fixes the problem you’re describing, though.

I converted your code to C, and ran it on the latest master. I can confirm that this issue happens when the window is put into fullscreen (green full screen mode button in the title bar), and persists when the window exits this fullscreen mode. It does however not happen when the window is maximized (fill desktop work area (double click on title bar)).

Please report this as a bug on Github if you have an account there. See reporting a bug.

C code:

#define GLAD_GL_IMPLEMENTATION
#include <glad/gl.h>
#define GLFW_INCLUDE_NONE
#include <GLFW/glfw3.h>

#include <stdio.h>
#include <stdlib.h>


// Make sure we always have a square viewport
void windowSizeCallback(GLFWwindow* window, int x, int y) {
    if (x > y) {
        glViewport(0, 0, y, y);
    } else {
        glViewport(0, y - x, x, x);
    }
}

int main(int argc, char** argv) {
    // Initialize the GLFW library
    if (!glfwInit()) {
        exit(-1);
    }
    
    // Create a windowed mode window and its OpenGL context
    GLFWwindow* window = glfwCreateWindow(200, 200, "Test Viewport", NULL, NULL);
    if (!window) {
        glfwTerminate();
        exit(-1);
    }


    // Use the frame buffer size callback instead of the window size callback
    glfwSetFramebufferSizeCallback(window, windowSizeCallback);
    
    glfwMakeContextCurrent(window);
    gladLoadGL(glfwGetProcAddress);

    while (!glfwWindowShouldClose(window)) {
        glClear(GL_COLOR_BUFFER_BIT);
        glBegin(GL_LINES);
        glVertex2i(-1, -1);
        glVertex2i(1, 1);
        glVertex2i(-1, 1);
        glVertex2i(1, -1);
        glEnd();
        glfwSwapBuffers(window);

        glfwWaitEvents();
    }
    
    glfwTerminate();
}

The bug has nothing to do with glViewport. It is caused by GLFW reporting an incorrect frame buffer size. Pretty much a result of the same that spawned multiple other similar bug reports. Logging the frame buffer size when in fullscreen in this program, reports 3600x2252 on my 3024x1964 screen.

Thanks for the confirmation. I will report a bug on GitHub. Could you just check one more thing for me: if you change the callback function to use a fixed viewport, e.g.:

glViewport(0, 0, 200, 200);

and you maximize the window, does the viewport fill the entire screen (i.e. the cross is stretched horizontally)? That’s what I’m seeing, which seems to indicate that not only is the framebuffer size reported incorrectly, but the call to glViewport also fails. Not sure if this is related to GLFW, but as said before, a similar GLUT program does work correctly.

I also converted my app to use the framebuffer callback as indeed the window size and frame buffer size differ on the Mac Retina screens (not on regular screens though).

Thanks again,

Wouter

Yes, but this actually has nothing to do with OpenGL either. It’s simply because the callback is never fired when you move into fullscreen. The frame buffer size callback is neither fired. The viewport is reset every time the frame buffer size changes. When the window is put into fullscreen, the size changes, but the callbacks are not called, so you’re not overriding the now reset viewport. So this is also a bug.

As far as I can see by logging to the terminal, the FramebufferSizeCallback is fired when the window is put into full screen, either when it is done by the user (green button) or programmatically (by means of glfwSetWindowMonitor). But after the callback is completed, in the next iteration of the main drawing loop, the viewport is reset again to the screen size, as can be seen from the example below (in C).

So, even though the event of going to full screen has already been handled, the system decides on its own to change the viewport in the next iteration. Can you confirm this strange behaviour?

// compile:
// cc -o test_glfw -lglfw -framework OpenGL -DGL_SILENCE_DEPRECATION test_glfw.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <GLFW/glfw3.h>
#include <OpenGL/GL.h>

// Make sure we always have a square viewport
void framebufferSizeCallback(GLFWwindow* window, int x, int y) {
    printf("Framebuffer size: %d x %d\n", x, y);
    if (x > y) {
        glViewport(0, 0, y, y);
    } else {
        glViewport(0, y - x, x, x);
    }
}

// Exit when a key is pressed
void keyCallback(GLFWwindow* window, int key, int scancode, int action, int mods) {
    glfwTerminate();
    exit(0);
}

int main(int argc, char** argv) {
    // Initialize the GLFW library
    if (!glfwInit()) {
        exit(-1);
    }
    
    // Create a windowed mode window and its OpenGL context
    GLFWwindow* window = glfwCreateWindow(200, 200, "Test Viewport", NULL, NULL);
    if (!window) {
        glfwTerminate();
        exit(-1);
    }

    // Make the window's context current
    glfwMakeContextCurrent(window);

    // Synchronize buffer swap interval with refresh rate
    glfwSwapInterval(1);

    // Set the callback function for resizing the window
    glfwSetFramebufferSizeCallback(window, framebufferSizeCallback);

    // Set the callback function for keypresses
    glfwSetKeyCallback(window, keyCallback);

    // Make the window full-screen
    GLFWmonitor *monitor = glfwGetPrimaryMonitor();
    const GLFWvidmode *mode = glfwGetVideoMode(monitor);
    glfwSetWindowMonitor(window, monitor, 0, 0, mode->width, mode->height, mode->refreshRate);
    
    while (!glfwWindowShouldClose(window)) {
        glClear(GL_COLOR_BUFFER_BIT);
        glBegin(GL_LINES);
        glVertex2i(-1, -1);
        glVertex2i(1, 1);
        glVertex2i(-1, 1);
        glVertex2i(1, -1);
        glEnd();
        glfwSwapBuffers(window);

        glfwPollEvents();

        // Check if viewport has changed
        static GLint previousViewport[4];
        GLint viewport[4];
        glGetIntegerv(GL_VIEWPORT, viewport);
        if (memcmp(previousViewport, viewport, sizeof(viewport))) {
            printf("Viewport set to (%d, %d, %d, %d)\n", viewport[0], viewport[1], viewport[2], viewport[3]);
            memcpy(previousViewport, viewport, sizeof(viewport));
        }
    }
    
    glfwTerminate();
    return 0;
}

Bug report made here.

You’re right about your observations. I must’ve remembered something wrong from yesterday’s testing.

Repeatedly setting the viewport in the loop works, though.

This issue is probably caused by the native frame buffer being resized after the callback is fired. I’ll look into GLFW’s source code for that. glfwGetWindowSize and glfwGetFramebufferSize return the same values as given to the callbacks.