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;
}