I would like to create a semi transparent white window in XLib, but the window is not semi translucent, it remains fully opaque. I use the compton compositor and there are transparent windows in the system, so the problem is in the code:
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <stdio.h>
int main(int argc, char* argv[])
{
Display* display = XOpenDisplay(NULL);
XVisualInfo vinfo;
XMatchVisualInfo(display, DefaultScreen(display), 32, TrueColor, &vinfo);
XSetWindowAttributes attr;
attr.colormap = XCreateColormap(display, DefaultRootWindow(display), vinfo.visual, AllocNone);
attr.border_pixel = 0;
attr.background_pixel = 0x80ffffff;
Window win = XCreateWindow(display, DefaultRootWindow(display), 0, 0, 300, 200, 0, vinfo.depth, InputOutput, vinfo.visual, CWColormap | CWBorderPixel | CWBackPixel, &attr);
XSelectInput(display, win, StructureNotifyMask);
GC gc = XCreateGC(display, win, 0, 0);
Atom wm_delete_window = XInternAtom(display, "WM_DELETE_WINDOW", 0);
XSetWMProtocols(display, win, &wm_delete_window, 1);
XMapWindow(display, win);
int keep_running = 1;
XEvent event;
while (keep_running) {
XNextEvent(display, &event);
switch(event.type) {
case ClientMessage:
if (event.xclient.message_type == XInternAtom(display, "WM_PROTOCOLS", 1) && (Atom)event.xclient.data.l[0] == XInternAtom(display, "WM_DELETE_WINDOW", 1))
keep_running = 0;
break;
default:
break;
}
}
XDestroyWindow(display, win);
XCloseDisplay(display);
return 0;
}
X11 expects pre-multiplied colours, i.e. real opaque colours need to be multiplied by the alpha value (and scaled accordingly, i.e. divided by 256 when channel widths is 8 bits). This format is easier to work with when you need to combine many levels. See formulas here. There's less computation when everything is pre-multiplied.
So you need to multiply each of your R, G and B channels by the alpha value (0x80) and divide by 256.
Setting the background to 0x80808080 gives the desired result:
Note the result is different from what #patthoyts suggests: here only the window proper is semi-transparent, the WM decoration stays opaque; there both the window proper and the decoration are made transparent by the WM (and the WM does the necessary colour blending).
You need to set _NET_WM_WINDOW_OPACITY. Here is a snippet to add in before you map the window:
double alpha = 0.8;
unsigned long opacity = (unsigned long)(0xFFFFFFFFul * alpha);
Atom XA_NET_WM_WINDOW_OPACITY = XInternAtom(display, "_NET_WM_WINDOW_OPACITY", False);
XChangeProperty(display, win, XA_NET_WM_WINDOW_OPACITY, XA_CARDINAL, 32,
PropModeReplace, (unsigned char *)&opacity, 1L);
Note you should add #include <X11/Xatom.h> to get the declaration of XA_CARDINAL.
I'm not entirely sure how stable this interface is. This appears to be a proposed extension to the Extended Window Manager Hints specification but has not made it into any final revision from what I can see. I know that this is how Tk implements transparency support on unix though.
The result looks like:
Related
I am trying to create a simple X11 window, which should display a PNG file with transparent areas. I want the window itself to have no (opaque) background, so that the transparent areas in the PNG shows what there is behind the window.
tl;dr I cannot put an image on a semi-transparent window; it gives "Bad Match".
I can successfully create a semi-transparent window using XCreateWindow and XMatchVisualInfo :
XSetWindowAttributes attr;
attr.colormap = XCreateColormap(display, DefaultRootWindow(display),
vinfo.visual, AllocNone);
attr.border_pixel = 0;
attr.background_pixel = 0x80800000; // Red, semi-transparent
Window window = XCreateWindow(display, DefaultRootWindow(display), 0, 0,
width, height, 0, vinfo.depth, InputOutput, vinfo.visual,
CWColormap | CWBorderPixel | CWBackPixel, &attr);
(Full source code below)
I then create an image using :
// "image32" is a generated image - see source code below
XImage *ximage = XCreateImage(display, visual, DefaultDepth(display,DefaultScreen(display)),
ZPixmap, 0, image32, width, height, 32, 0);
And display the image during the Expose event :
XPutImage(display, window, DefaultGC(display, 0), ximage,
0, 0, 0, 0, width, height);
I compile with gcc test.c -L/usr/X11R6/lib -lX11 -lXrandr -o test and run with ./test :
X Error of failed request: BadMatch (invalid parameter attributes)
Major opcode of failed request: 72 (X_PutImage)
Serial number of failed request: 11
Current serial number in output stream: 12
Note : If I replace the lines creating the window (XCreateWindow) with these :
Window window = XCreateSimpleWindow(display, RootWindow(display, 0), 0, 0,
width, height, 1, 0, 0);
It displays a window correctly; however, there is no transparency.
I read the docs about XCreateWindow, XPutImage, XCreateImage and tried playing around with multiple parameters, unsuccessfully.
I have read this SO question and tried playing around with color depth; since the docs mentionned "Bad Match" can be also thrown for incorrect visual, I have checked that the same visual was sent at all places in my code.
Any help is appreciated.
Thanks!
Full source code :
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
// Window size
int height = 256, width = 256;
XImage *CreateTrueColorImage(Display *display, Visual *visual)
{
int i, j;
unsigned char *image32=(unsigned char *)malloc(width*height*4);
unsigned char *p=image32;
for(i=0; i<width; i++)
{
for(j=0; j<height;j++)
{
*p++ = i;
*p++ = i;
*p++ = j;
*p++ = j; // alpha channel (should progressively get transparent towards left)
}
}
// Replacing "DefaultDepth(display,DefaultScreen(display))" with a hardcoded
// 24 or 32 still doesn't work with XCreateWindow. XCreateSimpleWindow works
// with hardcoded 24, but not 32.
return XCreateImage(display, visual, DefaultDepth(display,DefaultScreen(display)),
ZPixmap, 0, image32, width, height, 32, 0);
}
int main(int argc, char **argv)
{
XImage *ximage;
Display *display = XOpenDisplay(NULL);
Visual *visual = DefaultVisual(display, 0);
XVisualInfo vinfo;
XMatchVisualInfo(display, DefaultScreen(display), 32, TrueColor, &vinfo);
XSetWindowAttributes attr;
attr.colormap = XCreateColormap(display, DefaultRootWindow(display),
vinfo.visual, AllocNone);
attr.border_pixel = 0;
attr.background_pixel = 0x80800000; // Red, semi-transparent
//Window window = XCreateSimpleWindow(display, RootWindow(display, 0), 0, 0,
// width, height, 1, 0, 0);
Window window = XCreateWindow(display, DefaultRootWindow(display), 0, 0,
width, height, 0, vinfo.depth, InputOutput, vinfo.visual,
CWColormap | CWBorderPixel | CWBackPixel, &attr);
ximage = CreateTrueColorImage(display, vinfo.visual);
XSelectInput(display, window, ButtonPressMask|ExposureMask);
XMapWindow(display, window);
while(1)
{
XEvent event;
XNextEvent(display, &event);
switch(event.type)
{
case Expose:
XPutImage(display, window, DefaultGC(display, 0), ximage,
0, 0, 0, 0, width, height);
break;
case ButtonPress:
exit(0);
}
}
}
I managed to make it work by making two changes.
First, instead of using DefaultGC(display, 0) you should create a GC for your specific window.
GC gc = XCreateGC(display, window, 0, 0);
With that if you hardcode the depth of XCreateImage to 32 it should work correctly.
And you can also use the depth provided by XVisualInfo like so
XCreateImage(display, vinfo.visual, vinfo.depth,
ZPixmap, 0, image32, width, height, 32, 0);
My program is about drawing the Mandelbrot set. I discovered that when I click another window in the operating system (eg. Chrome, or text editor ...) my program clears the screen to black but continues drawing where it finished. It is during drawing. If the drawing is finished and I click another window then nothing clearing happens.
Is there a solution to fix this (eg. switching something on or off in SDL) without tracking all the pixels onto the screen and print the entire pixel matrix all the times?
Minimal code:
#include <stdlib.h>
#include <stdbool.h>
#include <SDL2/SDL.h>
enum {WIDTH = 1700, HEIGHT = 950};
int main(){
SDL_Init(SDL_INIT_VIDEO);
SDL_Window *window = SDL_CreateWindow("Minimal", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, WIDTH, HEIGHT, SDL_WINDOW_SHOWN);
SDL_Renderer *renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
SDL_RenderClear(renderer);
SDL_RenderPresent(renderer);
SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
SDL_Event event;
bool exit_ = false;
while (! exit_){
for (int i = 1; i < WIDTH; i++){
for (int j = 1; j < HEIGHT; j++){
SDL_RenderDrawPoint(renderer, i, j);
}
SDL_RenderPresent(renderer);
}
while (SDL_PollEvent(&event)){
if (event.type == SDL_QUIT) exit_ = true;
}
}
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
return 0;
}
(Note: I haven't got any window event handling in the program.)
I think you need to put SDL_RenderClear(Renderer) at the top of the while loop.
If you do like that, window will be never cleared after rendering.
For example if you don't clear the window, a moving dot will leave his traces..
I work woth SDL too, and i always do this
I'm trying to use the Cairo graphics library on Linux in C to make a pretty lightweight x11 GUI.
After trying very hard to follow the woefully incomplete guide that cairo gives for x11, this is the best I've got:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <cairo.h>
#include <cairo-xlib.h>
#include <X11/Xlib.h>
#include <X11/extensions/Xrender.h>
#include <X11/extensions/renderproto.h>
//This function should give us a new x11 surface to draw on.
cairo_surface_t* create_x11_surface(int x, int y)
{
Display* d;
Drawable da;
int screen;
cairo_surface_t* sfc;
if((d = XOpenDisplay(NULL)) == NULL)
{
printf("failed to open display\n");
exit(1);
}
screen = DefaultScreen(d);
da = XCreateSimpleWindow(d, DefaultRootWindow(d), 0, 0, x, y, 0, 0, 0);
XSelectInput(d, da, ButtonPressMask | KeyPressMask);
XMapWindow(d, da);
sfc = cairo_xlib_surface_create(d, da, DefaultVisual(d, screen), x, y);
cairo_xlib_surface_set_size(sfc, x, y);
return sfc;
}
int main(int argc, char** argv)
{
//create a new cairo surface in an x11 window as well as a cairo_t* to draw
//on the x11 window with.
cairo_surface_t* surface = create_x11_surface(300, 200);
cairo_t* cr = cairo_create(surface);
while(1)
{
//save the empty drawing for the next time through the loop.
cairo_push_group(cr);
//draw some text
cairo_select_font_face(cr, "serif", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);
cairo_set_font_size(cr, 32.0);
cairo_set_source_rgb(cr, 0, 0, 1.0);
cairo_move_to(cr, 10.0, 25.0);
if((argc == 2) && (strnlen(argv[1], 100) < 50))
cairo_show_text(cr, argv[1]);
else
cairo_show_text(cr, "usage: ./p1 <string>");
//put the drawn text onto the screen(?)
cairo_pop_group_to_source(cr);
cairo_paint(cr);
cairo_surface_flush(surface);
//pause for a little bit.
int c = getchar();
//change the text around so we can see the screen update.
for(int i = 0; i < strnlen(argv[1], 100); i++)
{
argv[1][i] = argv[1][i + 1];
}
if(c == 'q')
{
break;
}
}
cairo_surface_destroy(surface);
return 0;
}
On Linux systems that have Cairo installed, it can be compiled with
gcc -o myprog $(pkg-config --cflags --libs cairo x11) -std=gnu99 main.c
And it should be run with a single argument.
For reasons I don't understand at all, inserting the line
cairo_pop_group_to_source(cr);
cairo_paint(cr);
cairo_surface_write_to_png (surface, "hello.png"); //<--------- inserted
cairo_surface_flush(surface);
Puts something on the screen, but there are 2 problems:
Text that I draw with this method is persistent, creating a smearing effect.
I don't want some .png file mediating between my program and an x11 window. Data should be sent directly!
Several issues:
In X11, the X11 server doesn't save what you drew to a window, but instead sends an ExposeEvent to your window that tells it to redraw. This means you get a black window, because you do not handle this event.
getchar only gives you something after a line break, so just typing something won't help.
libX11 buffers stuff and only sends it to the X11 server when you wait for an event (or the buffer fills up). Since you never wait for an event, it never flushes. Calling XFlush explicitly helps.
The group that you push is useless. Just get rid of it.
Your code to move the string one direction to the left easily goes beyond the end of the string. You apparently know this already, because you 'fixed' this with a strnlen.
Here is a little better solution, but it still gives you an initially black window, because you draw to it before it is mapped:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <cairo-xlib.h>
#include <X11/Xlib.h>
//This function should give us a new x11 surface to draw on.
cairo_surface_t* create_x11_surface(Display *d, int x, int y)
{
Drawable da;
int screen;
cairo_surface_t* sfc;
screen = DefaultScreen(d);
da = XCreateSimpleWindow(d, DefaultRootWindow(d), 0, 0, x, y, 0, 0, 0);
XSelectInput(d, da, ButtonPressMask | KeyPressMask);
XMapWindow(d, da);
sfc = cairo_xlib_surface_create(d, da, DefaultVisual(d, screen), x, y);
return sfc;
}
int main(int argc, char** argv)
{
Display *d = XOpenDisplay(NULL);
if (d == NULL) {
fprintf(stderr, "Failed to open display\n");
return 1;
}
//create a new cairo surface in an x11 window as well as a cairo_t* to draw
//on the x11 window with.
cairo_surface_t* surface = create_x11_surface(d, 300, 200);
cairo_t* cr = cairo_create(surface);
char *text = argv[1];
size_t text_len = 0;
if (argc != 2)
text = NULL;
else
text_len = strlen(text);
while(1)
{
// Clear the background
cairo_set_source_rgb(cr, 0, 0, 0);
cairo_paint(cr);
//draw some text
cairo_select_font_face(cr, "serif", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);
cairo_set_font_size(cr, 32.0);
cairo_set_source_rgb(cr, 0, 0, 1.0);
cairo_move_to(cr, 10.0, 25.0);
if (text)
cairo_show_text(cr, text);
else
cairo_show_text(cr, "usage: ./p1 <string>");
cairo_surface_flush(surface);
XFlush(d);
//pause for a little bit.
int c = getchar();
//change the text around so we can see the screen update.
memmove(text, &text[1], text_len);
if (text_len > 0)
text_len--;
printf("got char %c\n", c);
if(c == 'q')
{
break;
}
}
// XXX: Lots of other stuff isn't properly destroyed here
cairo_surface_destroy(surface);
return 0;
}
Edit: Also, why exactly do you feel like cairo only gives you a woefully incomplete guide? It tells you how to get the cairo parts working and it also explains you some parts about X11, even though you should already know those if you want to use cairo-x11. That's none of its business. The guide you linked to even provides a complete, working and self-contained example: https://www.cypherpunk.at/files/2014/11/cairo_xlib_simple.c
I've you would have read the complete text of this "imcomplete guide" you would have seen that there is a link to the full sample: https://www.cypherpunk.at/files/2014/11/cairo_xlib_simple.c .
I was writing a X-server display code to show movement of balls from one pixel location to next location, which can be visible at the new window opened up after execution of the below code. While running the code, I can get 5 red color filled circles (considered as balls). The code can run pretty fast, real time computation is just like 2-3 ms, which is obvious, and on the window all the 5 balls are shown simultaneously.
I want to introduce some delay in between so that I can see the movement of ball from one pixel position to other. For doing this, what should I add to the given code? I hope some sort of timer or interrupt so that for some time the execution will be stopped for that time, while switching from one place to other and it can be visible at the window.
Code:
#include <stdio.h>
#include <X11/Xlib.h>
#include <math.h>
//Xserver variable declarations
Display *display;
Window window;
XSetWindowAttributes attributes;
XGCValues gr_values;
XFontStruct *fontinfo;
GC gr_context;
Visual *visual;
int depth;
int screen;
XEvent event;
XColor color, dummy;
main (argc, argv)
char *argv[];
int argc;
{
display = XOpenDisplay(NULL);
screen = DefaultScreen(display);
visual = DefaultVisual(display,screen);
depth = DefaultDepth(display,screen);
attributes.background_pixel = XWhitePixel(display,screen);
window = XCreateWindow( display,XRootWindow(display,screen),
200, 200, 750, 200, 5, depth, InputOutput,
visual ,CWBackPixel, &attributes);
XSetStandardProperties(display,window,"Welcome","Hi",None,NULL,0,NULL);
XSelectInput(display,window,ExposureMask | KeyPressMask) ;
fontinfo = XLoadQueryFont(display,"6x10");
XAllocNamedColor(display, DefaultColormap(display, screen),"red",
&color,&dummy);
gr_values.font = fontinfo->fid;
gr_values.foreground = color.pixel;
gr_context=XCreateGC(display,window,GCFont+GCForeground, &gr_values);
XFlush(display);
XMapWindow(display,window);
XFlush(display);
int i,j,a,a1,a2,a4,a5,b1,b2,b4,b5,b,h,w,angle1,angle2;
while(1){
XNextEvent(display,&event);
switch(event.type){
case Expose:
//Make a circle using arc, and then fill it with a color
a = 200; a1=520; a2=750;a4=400;a5=1050;
b = 200;b1=100; b2=300;b4=400;b5=500;
h = 10, w = 10;
angle1 = 0, angle2 = 360*64;
//Some delay I may need to introduce here ??
XFillArc(display, window, gr_context, a-(w/2), b-(h/2), w, h, angle1, angle2);
XFillArc(display, window, gr_context, a1-(w/2), b1-(h/2), w, h, angle1, angle2);
XFillArc(display, window, gr_context, a2-(w/2), b2-(h/2), w, h, angle1, angle2);
XFillArc(display, window, gr_context, a4-(w/2), b4-(h/2), w, h, angle1, angle2);
XFillArc(display, window, gr_context, a5-(w/2), b5-(h/2), w, h, angle1, angle2);
break;
case KeyPress:
XCloseDisplay(display);
exit(0);
}
}
}
Is there a way to create a window with Xlib which only display the border lines, title, close button and that you can move with the mouse? The content of the window must be empty (or "totally transparent", although "transparency" sounds more like an effect I don't need). Basically the window should show the background area.
I'm not sure if it is what you want, but following code creates an X window with transparent background but still using the window decoration of your window manager.
It will only work though if your X11 and graphics hardware configuration supports visuals with a depth of 32 bit.
#include <X11/Xlib.h>
#include <X11/Xutil.h>
int main(int argc, char* argv[])
{
Display* display = XOpenDisplay(NULL);
XVisualInfo vinfo;
XMatchVisualInfo(display, DefaultScreen(display), 32, TrueColor, &vinfo);
XSetWindowAttributes attr;
attr.colormap = XCreateColormap(display, DefaultRootWindow(display), vinfo.visual, AllocNone);
attr.border_pixel = 0;
attr.background_pixel = 0;
Window win = XCreateWindow(display, DefaultRootWindow(display), 0, 0, 300, 200, 0, vinfo.depth, InputOutput, vinfo.visual, CWColormap | CWBorderPixel | CWBackPixel, &attr);
XSelectInput(display, win, StructureNotifyMask);
GC gc = XCreateGC(display, win, 0, 0);
Atom wm_delete_window = XInternAtom(display, "WM_DELETE_WINDOW", 0);
XSetWMProtocols(display, win, &wm_delete_window, 1);
XMapWindow(display, win);
int keep_running = 1;
XEvent event;
while (keep_running) {
XNextEvent(display, &event);
switch(event.type) {
case ClientMessage:
if (event.xclient.message_type == XInternAtom(display, "WM_PROTOCOLS", 1) && (Atom)event.xclient.data.l[0] == XInternAtom(display, "WM_DELETE_WINDOW", 1))
keep_running = 0;
break;
default:
break;
}
}
XDestroyWindow(display, win);
XCloseDisplay(display);
return 0;
}