X11 window black fullscreen bug?

Started by
5 comments, last by Finalspace 4 years, 9 months ago

Hi there,

i am fighting against a weird bug in X11 where i only see a fullscreen black screen with my window behind it - but only when i do software rendering.
If you tab switch into another window, or click outside of your actual window area, the desktop are shown - but my window are not.

Only when i have a window manager with task icon preview (Linux Mint Cinnamon), i can actually see the window - but only when i hover it over the icon in the taskbar O_o.

The last time i had run my x11 application was on my previous ubuntu linux installation. Now i have linux mint with cinnamon - and it does not work anymore.

 

I have no idea why that is the case, nor how to fix it. Can you please have a look at my posted source, maybe i do something horrible wrong?

 

I tried recording the issue with OBS - but weirdly the bug is not present in the OBS recording, the window is  just shown just fine O_o.

So i did a real screen capture with my iphone and uploaded it:

 

Here is my code (main.c / C99):


#if !defined(_XOPEN_SOURCE)
#	define _XOPEN_SOURCE 600
#endif
#if !defined(_DEFAULT_SOURCE)
#	define _DEFAULT_SOURCE 1
#endif
#if !defined(__STDC_FORMAT_MACROS)
#	define __STDC_FORMAT_MACROS
#endif
#if !defined(__STDC_LIMIT_MACROS)
#	define __STDC_LIMIT_MACROS
#endif
#if !defined(_LARGEFILE_SOURCE)
#	define _LARGEFILE_SOURCE
#endif
#if !defined(_LARGEFILE64_SOURCE)
#	define _LARGEFILE64_SOURCE
#endif
#if !defined(_FILE_OFFSET_BITS)
#	define _FILE_OFFSET_BITS 64
#endif

#include <stdint.h> // uint32_t, ...
#include <stddef.h> // size_t
#include <stdbool.h> // bool
#include <stdarg.h> // va_start, va_end, va_list, va_arg
#include <stdio.h>
#include <malloc.h>
#include <limits.h> // UINT32_MAX, ...
#include <assert.h>

#include <sys/time.h>
#include <unistd.h>
#include <time.h>

#include <X11/X.h> // Window
#include <X11/Xlib.h> // Display
#include <X11/Xutil.h> // XVisualInfo
#include <X11/Xatom.h> // XA_CARDINAL

#define ArrayCount(arr) (sizeof(arr) / (arr)[0])

typedef struct BackBuffer {
    uint32_t width;
    uint32_t height;
    uint32_t pixelStride;
    uint32_t lineWidth;
    uint32_t *pixels;
    XImage *image;
} BackBuffer;

typedef struct WindowState {
    BackBuffer backbuffer;
    Window root;
    Window window;
    Colormap colorMap;
    GC gc;
    Display *display;
    Visual *visual;
    int colorDepth;
    int screen;
    int width;
    int height;
    Atom wmProtocols;
    Atom wmDeleteWindow;
    Atom netWMPing;
    Atom netWMPid;
    bool isRunning;
} WindowState;

void FillBackbuffer(BackBuffer *backbuffer, uint32_t color) {
    for (size_t i = 0; i < backbuffer->width * backbuffer->height; ++i) {
        backbuffer->pixels[i] = color;
    }
}

void InitBackBuffer(Display *display, Visual *visual, BackBuffer *backbuffer, uint32_t w, uint32_t h) {
    backbuffer->width = w;
    backbuffer->height = h;
    backbuffer->pixelStride = sizeof(uint32_t);
    backbuffer->lineWidth = backbuffer->width * backbuffer->pixelStride;

    size_t size = backbuffer->lineWidth * backbuffer->height;
    assert(backbuffer->pixels == NULL);
    backbuffer->pixels = (uint32_t *)malloc(size);
    FillBackbuffer(backbuffer, 0xFF000000);

    assert(backbuffer->image == NULL);
    backbuffer->image = XCreateImage(display, visual, 24, ZPixmap, 0, (char *)backbuffer->pixels, backbuffer->width, backbuffer->height, 32, (int)backbuffer->lineWidth);
}

void FreeBackBuffer(BackBuffer *backbuffer) {
    if (backbuffer->image){
        XFree(backbuffer->image);
        backbuffer->image = NULL;
    }
    if (backbuffer->pixels){
        free(backbuffer->pixels);
        backbuffer->pixels = NULL;
    }
}

void ResizeBackBuffer(Display *display, Visual *visual, BackBuffer *backbuffer, uint32_t w, uint32_t h) {
    FreeBackBuffer(backbuffer);
    InitBackBuffer(display, visual, backbuffer, w, h);
}

void HandleEvent(WindowState *windowState, XEvent *ev) {
    switch (ev->type) {
        case ConfigureNotify:
        {
            uint32_t w = (uint32_t)ev->xconfigure.width;
            uint32_t h = (uint32_t)ev->xconfigure.height;
            windowState->width = w;
            windowState->height = h;
            ResizeBackBuffer(windowState->display, windowState->visual, &windowState->backbuffer, w, h);
        } break;

        case ClientMessage:
        {
            if (ev->xclient.message_type == windowState->wmProtocols) {
                const Atom protocol = (Atom)ev->xclient.data.l[0];
                if (protocol != None) {
                    if (protocol == windowState->wmDeleteWindow) {
                        windowState->isRunning = false;
                    }
                } else if (protocol == windowState->netWMPing) {
                    XEvent reply = *ev;
                    reply.xclient.window = windowState->root;
                    XSendEvent(windowState->display, windowState->root, False, SubstructureNotifyMask | SubstructureRedirectMask, &reply);
                }
            }
        }  break;

        case Expose:
        {
        }  break;

        default:
            break;
    }
}

void Release(WindowState *windowState) {
    if (windowState->gc) {
        XFree(windowState->gc);
        windowState->gc = NULL;
    }
    if (windowState->window) {
        XUnmapWindow(windowState->display, windowState->window);
        XDestroyWindow(windowState->display, windowState->window);
        windowState->window = 0;
    }
    if (windowState->colorMap) {
        XFreeColormap(windowState->display, windowState->colorMap);
        windowState->colorMap = 0;
    }
    if (windowState->display) {
        XCloseDisplay(windowState->display);
        windowState->display = NULL;
    }
}

void ProcessEvents(WindowState *windowState) {
    XEvent ev;
    while (XPending(windowState->display)) {
        XNextEvent(windowState->display, &ev);
        HandleEvent(windowState, &ev);
    }
}

bool Init(WindowState *windowState) {
    windowState->display = XOpenDisplay(":0");
    if (!windowState->display) {
        printf("ERROR: No display found!\n");
        return(false);
    }

    windowState->screen = XDefaultScreen(windowState->display);

    windowState->root = XRootWindow(windowState->display, windowState->screen);

    // Visual
    XVisualInfo vinfo = {0};
    if (!XMatchVisualInfo(windowState->display, windowState->screen, 32, DirectColor, &vinfo)) {
        if (!XMatchVisualInfo(windowState->display, windowState->screen, 24, DirectColor, &vinfo)) {
            Release(windowState);
            return(false);
        }
    }

    windowState->visual = vinfo.visual;
    windowState->colorDepth = vinfo.depth;
    windowState->colorMap = XCreateColormap(windowState->display, windowState->root, windowState->visual, AllocNone);

    XSetWindowAttributes swa = {0};
    swa.colormap = windowState->colorMap;
    swa.event_mask =
        StructureNotifyMask |
        ExposureMask | FocusChangeMask | VisibilityChangeMask |
        EnterWindowMask | LeaveWindowMask | PropertyChangeMask |
        KeyPressMask | KeyReleaseMask |
        ButtonPressMask | ButtonReleaseMask | PointerMotionMask | ButtonMotionMask;
    swa.background_pixel = BlackPixel(windowState->display, windowState->screen);
    swa.border_pixel = WhitePixel(windowState->display, windowState->screen);
    swa.bit_gravity = NorthWestGravity;
    swa.win_gravity = NorthWestGravity;

    int flags = CWColormap | CWBackPixel | CWBorderPixel | CWEventMask |  CWBitGravity | CWWinGravity;

    int windowX = 0;
    int windowY = 0;
    int windowWidth = 400;
    int windowHeight = 400;

    windowState->window = XCreateWindow(windowState->display,
                                        windowState->root,
                                        windowX,
                                        windowY,
                                        windowWidth,
                                        windowHeight,
                                        0,
                                        windowState->colorDepth,
                                        InputOutput,
                                        windowState->visual,
                                        flags,
                                        &swa);

    if (!windowState->window) {
        Release(windowState);
        return(false);
    }

    windowState->width = windowWidth;
    windowState->height = windowHeight;

    XSelectInput(windowState->display, windowState->window, KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | ButtonMotionMask);

    // Window manager atoms
    windowState->wmDeleteWindow = XInternAtom(windowState->display, "WM_DELETE_WINDOW", False);
    windowState->netWMPid = XInternAtom(windowState->display, "_NET_WM_PID", False);
    windowState->netWMPing = XInternAtom(windowState->display, "_NET_WM_PING", False);
    windowState->wmProtocols = XInternAtom(windowState->display, "WM_PROTOCOLS", False);

    Atom protocols[] = {
        windowState->wmDeleteWindow,
        windowState->netWMPing
    };
    XSetWMProtocols(windowState->display, windowState->window, protocols, ArrayCount(protocols));

    const long pid = getpid();
    XChangeProperty(windowState->display, windowState->window, windowState->netWMPid, XA_CARDINAL, 32, PropModeReplace, (unsigned char*)&pid, 1);

    XMapWindow(windowState->display, windowState->window);
    XFlush(windowState->display);

    InitBackBuffer(windowState->display, windowState->visual, &windowState->backbuffer, windowState->width, windowState->height);

    windowState->gc = XCreateGC(windowState->display, windowState->window, 0, 0);
    XPutImage(windowState->display, windowState->window, windowState->gc, windowState->backbuffer.image, 0, 0, 0, 0, windowState->backbuffer.width, windowState->backbuffer.height);
    XSync(windowState->display, False);

    return(true);
}

void ColorTimeUpdate(float *t, int *d, const float speed) {
    *t += (float)*d * speed;
    if (*t <= 0.0f) {
        *t = 0.0f;
        *d = -*d;
    } else if (*t >= 1.0f) {
        *t = 1.0f;
        *d = -*d;
    }
}

static long
timestamp(void)
{
    struct timeval tv;
    if (gettimeofday(&tv, NULL) < 0) return 0;
    return (long)((long)tv.tv_sec * 1000 + (long)tv.tv_usec/1000);
}

static void
sleep_for(long t)
{
    struct timespec req;
    const time_t sec = (int)(t/1000);
    const long ms = t - (sec * 1000);
    req.tv_sec = sec;
    req.tv_nsec = ms * 1000000L;
    while(-1 == nanosleep(&req, &req));
}

int main()
{
    const long DTIME = 1000 / 60;

    WindowState windowState = {0};
    float tR = 0.0f;
    float tG = 0.0f;
    float tB = 0.0f;
    int dR = 1;
    int dG = 1;
    int dB = 1;
    if (Init(&windowState)) {
        windowState.isRunning = true;
        long timeStart = timestamp();
        while (windowState.isRunning) {
            ProcessEvents(&windowState);

            uint8_t r = (uint32_t)(tR * 255.0f + 0.5f);
            uint8_t g = (uint32_t)(tG * 255.0f + 0.5f);
            uint8_t b = (uint32_t)(tB * 255.0f + 0.5f);
            uint8_t a = 255;
            uint32_t color = (a << 24) | (r << 16) | (g << 8) | (b << 0);
            FillBackbuffer(&windowState.backbuffer, color);

            XPutImage(windowState.display, windowState.window, windowState.gc, windowState.backbuffer.image, 0, 0, 0, 0, windowState.backbuffer.width, windowState.backbuffer.height);
            XFlush(windowState.display);

            ColorTimeUpdate(&tR, &dR, 0.1f);
            ColorTimeUpdate(&tG, &dG, 0.01f);
            ColorTimeUpdate(&tB, &dB, 0.001f);

            long timeEnd = timestamp();
            long dt = timeEnd - timeStart;
            timeStart = timeEnd;
            if (dt < DTIME){
                sleep_for(DTIME - dt);
            }
        }
        Release(&windowState);
    }
    return 0;
}

 

Advertisement

Are the fall through's in HandleEvent() on purpose ?

 

26 minutes ago, Green_Baron said:

Are the fall through's in HandleEvent() on purpose ?

 

Its not. Forgot to copy the break at the end from my actual code.

I corrected the source. Thanks for the catch.

I have never programmed things like this myself, but you have awakened my hunting instinct:

There is an error you are probably aware of ?

"XIO:  fatal IO error 11 (Resource temporarily unavailable) on X server ":0" after 576 requests (396 known processed) with 0 events remaining."

Number of requests probably connected to the refresh cycle ... (Edit: Debian 10, xfce, this is)

1 hour ago, Green_Baron said:

I have never programmed things like this myself, but you have awakened my hunting instinct:

There is an error you are probably aware of ?

"XIO:  fatal IO error 11 (Resource temporarily unavailable) on X server ":0" after 576 requests (396 known processed) with 0 events remaining."

Number of requests probably connected to the refresh cycle ... (Edit: Debian 10, xfce, this is)

Yep, i saw that error too - results in never getting to the event when the window is closed :(

I fixed it. The error was in the Visual and Colormap request.

Also it looks like that XSelectInput requires StructureNotifyMask and ExposureMask, otherwise you will never get a ConfigureNotify event coming in - even though you had both already set in event_mask O_o -> X11 is weird.

 


#if 0
// Wrong?
XVisualInfo vinfo = {0};
if (!XMatchVisualInfo(windowState->display, windowState->screen, 32, DirectColor, &vinfo)) {
  if (!XMatchVisualInfo(windowState->display, windowState->screen, 24, DirectColor, &vinfo)) {
    Release(windowState);
    return(false);
  }
}
windowState->colorDepth = vinfo.depth;
windowState->visual = vinfo.visual;
windowState->colorMap = XCreateColormap(windowState->display, windowState->root, windowState->visual, AllocNone);
#else
windowState->colorDepth = XDefaultDepth(windowState->display, windowState->screen);
windowState->visual = XDefaultVisual(windowState->display, windowState->screen);
windowState->colorMap = XDefaultColormap(windowState->display, windowState->screen);
#endif

 

This topic is closed to new replies.

Advertisement