I am designing an app that having the ID of an X11 window to draw a rectangle.
The problem I have is that I can not draw nothing in the window.
Code
One obvious error is that you did not select ExposureMask in call to XSelectInput, so you will not receive Expose event you are waiting for.
Other possible problem is not setting foreground drawing color of the GC by XSetForegroundColor, default is black. And using default gc of screen may fail if the window has different color depth or different visual. Other important GC attributes issubwindow_mode (whether to draw over child windows).
Still, after I did those changes, the program only works for me (draws a rectangle) on root window and xev, but not for xterm, no expose events.
This is my fixed version:
#include <X11/Xlib.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
int main (int argc, char *argv[]){
Window win;
Display *display;
XEvent e;
display = XOpenDisplay(NULL);
if(display==NULL){
fprintf(stderr,"Cannot open Display\n");
exit(1);
}
int s = DefaultScreen(display);
//unsigned long *id = (unsigned long*)(argv[1]);
sscanf(argv[1], "0x%x", &win);
if(!XSelectInput(display,win,StructureNotifyMask | ExposureMask)){
fprintf(stderr,"Cannot select Display\n");
return -1;
}
if(!XMapWindow(display,win)){
fprintf(stderr,"Cannot map Display\n");
return -1;
}
XGCValues gcv;
gcv.subwindow_mode = IncludeInferiors;
GC gc = XCreateGC(display, win, GCSubwindowMode,
&gcv);
XSetForeground(display, gc, 0xff00ff00);
XSetPlaneMask(display, gc, 0xffffffff);
while (1) {
puts("waiting for event\n");
XNextEvent(display, &e);
puts("got event\n");
if (e.type == Expose) {
printf("drawing\n");
XFillRectangle(display, win, gc, 20, 20, 100, 100);
}
if (e.type == KeyPress)
break;
}
return 0;
}
Related
Sorry for the poor title, it wasn't easy to describe it better. I am learning C and as a toy project I am building a simple status bar for my window manager. Basically it just outputs text to the root X11 window name, which gets picked up by the wm (DWM).
Now I wanted to add simple click detected, so that I can do something when a specific area is clicked. The polling of the mouse happens in a while loop; and the update of the status bar also lives in the while loop, but with a different (less frequent) interval. Everything works, except that no other X11 applications are receiving the mouse clicks I listen to in my while loop.
I used XNextEvent first but found out that was blocking the while loop if the mouse was idle. Then I learned about XCheckWindowEvent which returns a boolean if the event I want to poll matches, then I can handle it. But the loop will not pause when the mouse is idle (which is most of the time).
However since doing so, whenever I move the mouse, I cannot click on any other window anymore. The cursor moves just fine, but clicks do not seem to work.
Am I making any sense?
This is the listing of my program (slightly simplified as for the status setting part, since that probably will distract).
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <wchar.h>
#include <unistd.h>
#include <fcntl.h>
#include <X11/Xlib.h>
#define EVTMASK (ButtonPressMask | PointerMotionMask)
void set_status(Display *display, Window window, char *str);
void process_mouse(Display *display, Window window, XEvent xevent);
char *key_name[] = {
"first",
"second (or middle)",
"third"
};
int
main(void)
{
const int MSIZE = 1024;
Display *display;
XEvent xevent;
Window window;
char *status;
char *bg_color = "#000000";
char *clr_yellow = "#ecbe7b";
time_t previousTime = time(NULL);
time_t interval_status = 1;
time_t currentTime;
if (!(display = XOpenDisplay(NULL))) {
fprintf(stderr, "Cannot open display.\n");
return EXIT_FAILURE;
}
// Setup X11 for mouse grabbing
window = DefaultRootWindow(display);
XAllowEvents(display, AsyncBoth, CurrentTime);
XGrabPointer(display,
window,
1,
PointerMotionMask | ButtonPressMask | ButtonReleaseMask ,
GrabModeAsync,
GrabModeAsync,
None,
None,
CurrentTime);
status = (char*) malloc(sizeof(char)*MSIZE);
if(!status)
return EXIT_FAILURE;
while(1)
{
process_mouse(display, window, xevent);
if((time(¤tTime) - previousTime) >= interval_status)
{
int ret = snprintf(status, MSIZE, "^b%s^^c%s^%s Status me!", bg_color, clr_yellow);
set_status(display, window, status);
previousTime += interval_status;
}
}
return 0;
}
void
set_status(Display *display, Window window, char *str)
{
XStoreName(display, window, str);
XSync(display, False);
}
void
process_mouse(Display *display, Window window, XEvent xevent)
{
if(XCheckWindowEvent(display, window, EVTMASK, &xevent)) {
switch (xevent.type) {
case MotionNotify:
printf("Mouse move : [%d, %d]\n", xevent.xmotion.x_root, xevent.xmotion.y_root);
break;
case ButtonPress:
printf("Button pressed : %s\n", key_name[xevent.xbutton.button - 1]);
break;
case ButtonRelease:
printf("Button released : %s\n", key_name[xevent.xbutton.button - 1]);
break;
}
XPutBackEvent(display, &xevent);
}
}
Open question: should I go back to the NextEvent method and listen to the mouse on a different thread?
I can only get events after the window has moved, not while it is moving.
I want to get moving events in real time. Please,what should I do?
#include <X11/X.h>
#include <X11/Xlib.h>
#include <stdio.h>
int main(int argv, char **agrc)
{
Display *dpy = XOpenDisplay(NULL);
Window w = XCreateSimpleWindow(dpy, RootWindow(dpy, DefaultScreen(dpy)), 10, 10, 800, 400, 1, BlackPixel(dpy, DefaultScreen(dpy)), WhitePixel(dpy, DefaultScreen(dpy)));
XSelectInput(dpy, w, StructureNotifyMask);
XMapWindow(dpy, w);
XEvent event;
for (;;)
{
XNextEvent(dpy, &event);
if(event.type == ConfigureNotify)
{
XConfigureEvent x = event.xconfigure;
printf("config[%d]{%d,%d,%d,%d}\n", x.window, x.x, x.y, x.width, x.height);
}
}
XDestroyWindow(dpy, w);
XCloseDisplay(dpy);
return 0;
}
I would like to open a "dock type" window without its title bar with x11 in C and I want to capture a key press (Ctrl-q) to close it. The actual code does not capture the key presses to the active window as they are all sent and printed in the terminal. Almost all the Ctrl key presses are somehow not detected. The only key presses that actually work on the opened window are Ctrl-c. I am aware of the _MOTIF_WM_HINTS property but it has the disadvantage to display a window icon in the panel to show a new opened window.
How can I have a "dock type" window while not losing the ability to detect key presses to the active window?
Here is the code basically copied from two other posts here and here
#include <stdio.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#include <X11/keysym.h>
int main(int argc, char **argv)
{
Display *dpy = XOpenDisplay(0);
int blackColor = BlackPixel(dpy, DefaultScreen(dpy));
int whiteColor = WhitePixel(dpy, DefaultScreen(dpy));
Window w = XCreateSimpleWindow(dpy, DefaultRootWindow(dpy), 0, 0,
200, 100, 0, blackColor, blackColor);
//Tell X Server to send MapNotify events
XSelectInput(dpy, w, StructureNotifyMask | KeyPressMask);
Atom window_type = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False);
long value = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DOCK", False);
XChangeProperty(dpy, w, window_type, 4, 32,
PropModeReplace,
(unsigned char *)&value, 1);
//Make window appear
XMapWindow(dpy, w);
//Graphics Context
GC gc = XCreateGC(dpy, w, 0, 0);
//Set white color for drawing
XSetForeground(dpy, gc, whiteColor);
//Wait for the MapNotify event
for(;;) {
XEvent e;
XNextEvent(dpy, &e);
if (e.type == MapNotify) {
break;
}
}
//Draw the line
XDrawLine(dpy, w, gc, 10, 60, 180, 20);
//Send the "DrawLine" request to the server
XFlush(dpy);
char text[255];
XEvent e;
KeySym key;
int numKeys = 0;
for(;;) {
XNextEvent(dpy, &e);
if(e.type == KeyPress) {
if((numKeys = XLookupString(&e.xkey, text, 255, &key, 0))) {
if(e.xkey.state == ControlMask && key == XK_q) {
printf("CTRL-Q\n");
break;
}
}
}
}
if (dpy && gc) XFreeGC(dpy, gc);
if (dpy && w) XDestroyWindow(dpy, w);
if (dpy) XCloseDisplay(dpy);
return 0;
}
Compiled with:
gcc main.c -L/usr/X11R6/lib -lX11 -lm && ./a.out
I've also looked at the _NET_WM_STRUT_PARTIAL property but I haven't been succesful at implementing it.
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 tried create window with gl 3.3 context, but it's eating cpu and lagging when I try to resize it.
My Code:
#include <stdlib.h>
#include <stdio.h>
#include <X11/Xlib.h>
#include <GL/gl.h>
#include <GL/glx.h>
#include <GL/glxext.h>
#include <GL/glext.h>
struct _WGelWindowX11{
Display *XDspl;
Window XWin;
Colormap XCMap;
GLXContext glContext;
};
typedef struct _WGelWindowX11 WGelWindowX11;
#define WGEL_WINDOWX11(obj) ((WGelWindowX11*)obj)
int aiAttr[]={
GLX_X_RENDERABLE, True,
GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR,
GLX_RED_SIZE, 8,
GLX_GREEN_SIZE, 8,
GLX_BLUE_SIZE, 8,
GLX_ALPHA_SIZE, 8,
GLX_DEPTH_SIZE, 24,
GLX_DOUBLEBUFFER, True,
None
};
int aiAttrGL[]={
GLX_CONTEXT_MAJOR_VERSION_ARB, 3,
GLX_CONTEXT_MINOR_VERSION_ARB, 3,
None
};
void *WGelWindowNew(void){
WGelWindowX11 *Result=malloc(sizeof(WGelWindowX11));
Result->XDspl=XOpenDisplay(NULL);
if (!Result->XDspl){
perror("Cannot open display.\n");
return NULL;
}
//Gettin' matchin' framebuffer configuration
int iFBCount;
GLXFBConfig *pFBConf=glXChooseFBConfig(Result->XDspl, DefaultScreen(Result->XDspl), aiAttr, &iFBCount);
if(!pFBConf){
perror("Cannot get FBConfig.\n");
return NULL;
}
int iBestFBC=-1, iWorstFBC=-1, iBestNSampl=-1, iWorstNSampl=999,iSamplBuff, iSampl, i;
for(i=0; i<iFBCount; i++){
XVisualInfo *vi=glXGetVisualFromFBConfig(Result->XDspl, pFBConf[i]);
if (vi){
glXGetFBConfigAttrib(Result->XDspl, pFBConf[i], GLX_SAMPLE_BUFFERS, &iSamplBuff);
glXGetFBConfigAttrib(Result->XDspl, pFBConf[i], GLX_SAMPLES, &iSampl);
if (iBestFBC<0 || iSamplBuff && iSampl>iBestNSampl) iBestFBC=i, iBestNSampl=iSampl;
if (iWorstFBC<0 || !iSamplBuff || iSampl<iWorstNSampl) iWorstFBC=i, iWorstNSampl=iSampl;
}
XFree(vi);
}
GLXFBConfig FBConfBest=pFBConf[iBestFBC];
XFree(pFBConf);
XVisualInfo *XVslnfo=glXGetVisualFromFBConfig(Result->XDspl, FBConfBest);
XSetWindowAttributes XWinAttr;
Result->XCMap=XCreateColormap(Result->XDspl, RootWindow(Result->XDspl, XVslnfo->screen), XVslnfo->visual, AllocNone);
XWinAttr.colormap=Result->XCMap;
XWinAttr.background_pixmap=None;
XWinAttr.border_pixel=0;
XWinAttr.event_mask=StructureNotifyMask|ExposureMask|KeyPressMask;
Atom wmDeleteMessage=XInternAtom(Result->XDspl, "WM_DELETE_WINDOW", FALSE);
Result->XWin=XCreateWindow(Result->XDspl, RootWindow(Result->XDspl, XVslnfo->screen), 0, 0, 300, 300, 0, XVslnfo->depth, InputOutput, XVslnfo->visual, CWBorderPixel|CWColormap|CWEventMask, &XWinAttr);
if(!Result->XWin){
perror("Cannot create window.\n");
return NULL;
}
XFree(XVslnfo);
XStoreName(Result->XDspl, Result->XWin, "WGel Window" );
XSetWMProtocols(Result->XDspl, Result->XWin, &wmDeleteMessage, 1);
XMapWindow(Result->XDspl, Result->XWin);
PFNGLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribs=(PFNGLXCREATECONTEXTATTRIBSARBPROC)glXGetProcAddress((GLubyte*)"glXCreateContextAttribsARB");
Result->glContext=glXCreateContextAttribs(Result->XDspl, FBConfBest, 0, True, aiAttrGL);
if(!Result->glContext){
perror("Cannot create GL 3.3 context\n");
return NULL;
}
glXMakeCurrent(Result->XDspl, Result->XWin, Result->glContext);
glClearColor(1.0f, 1.0f, 1.0f, 0.0f);
return (void*)Result;}
Display *WGelX11GetDisplay(ptr window){
return WGEL_WINDOWX11(window)->XDspl;}
Window *WGelX11GetWindow(ptr window){
return &(WGEL_WINDOWX11(window)->XWin);
}
int main(void){
ptr frmMain=WGelWindowNew();
Display *dpy=WGelX11GetDisplay(frmMain);
XEvent e;
while(XNextEvent(dpy, &e)>=0){
if ((!XPending(dpy))&&(e.type==Expose)){
glClear(GL_COLOR_BUFFER_BIT);
glXSwapBuffers(dpy, *WGelX11GetWindow(frmMain));
}
if (e.type==ClientMessage){
XDestroyWindow(dpy, e.xclient.window);
break;
}
}
XCloseDisplay(dpy);
return EXIT_SUCCESS;}
GTK+ windows are not lagging that hard when I resize them, so I suppose there should be a way to get rid of that lagging.
Firstly, you have to ignore all Expose event and handle only the last one.
You can do like :
XNextEvent(dpy, &e);
switch (e.type)
{
case Expose:
/* Unless this is the last contiguous expose,
* don’t draw the window */
if (e.xexpose.count == 0)
{
// DRAW THINGS
}
break;
case ClientMessage:
XDestroyWindow(dpy, e.xclient.window);
break;
default:
break;
}
For better handling of Resizing, you have to catch the ConfigureNotify event.
Probably the same reason as I wrote about here: Is there a way to change ConfigureNotify event frequency in X11?
The basic fix: do not repaint at every resize event, your mouse generates events a lot faster than you can repaint the screen.