User avatar
ab1jx
Posts: 868
Joined: Thu Sep 26, 2013 1:54 pm
Location: Heath, MA USA
Contact: Website

Another silly xlib demo program

Thu Feb 16, 2017 4:53 pm

I read the first 7 chapters of the Xlib Programming Manual and I was still bothered by archaic things like colormaps. I haven't worked with any hardware that needed them in 20 years. What the text didn't say is that you can mostly ignore them. As long as you have 24 bit or more color depth and the window is TrueColor or DirectColor it just works.

Xlib has been around since about 1985 https://en.wikipedia.org/wiki/Xlib but I like to get to a low level when I want speed and efficiency. This draws and erases 20 lines per second and still uses under 0.5% CPU on my Pi 3B. But some things are broken now and not maintained, like I spent half a day trying to get XSetArcMode() to work and gave up. I wanted Pacman. :)
rlines_top.png
rlines and top
rlines_top.png (46.5 KiB) Viewed 1653 times
My Makefile as before works in both Raspbian and OpenBSD

Code: Select all

rlines: rlines.c
	gcc -O -Wall -g -o rlines -I/usr/X11R6/include/ -I/usr/include \
	-L/usr/X11R6/lib/ -lX11 rlines.c
And the program. ctrl-C in the controlling terminal (emulator) to stop. It's impressive at 1920x1080 (change WWIDTH and WHEIGHT).

Code: Select all

/*
  My second recreational X activity. Based on Tronche's tutorial.
  Copyright (c) 2017, Alan Corey, ab1jx, public domain
*/

#include <X11/Xlib.h> // Every Xlib program must include this
#include <assert.h>   // I include this to test return values the lazy way
#include <unistd.h>   // So we got the profile for 10 seconds
#include <stdio.h> 
#include <stdlib.h> 
#include <stdint.h> // for int16_t, int32_t, unambiguous
#include <time.h>  // time_t to seed random()

#define WWIDTH 320
#define WHEIGHT 200
#define NLINES 50  // size of lines[] array

Display *dpy;  // *struct
int blackcolor,whitecolor;
Window w;  // ID #
GC gc;
XEvent e; // a struct
XSetWindowAttributes setwinattr; // another struct

// to xor the lines for removing, we keep coords and color for each
struct linerec {
   uint16_t x1;
   uint16_t y1;
   uint16_t x2;
   uint16_t y2;
   uint32_t color;
  };

struct linerec lines[NLINES];

double myrand(void) {  // convenience: BASICish  0..1 float random
  return ((random() * 1.0) / (RAND_MAX * 1.0));
}

void randlines(void) {   // payload
  int pos = 0; // position in lines[] array
  for (pos=0; pos<NLINES; pos++) {  // init colors to 0 as unused flag
    lines[pos].color = 0;
  }
  while (1) {
    if (lines[pos].color != 0) { // old line exists
      XSetForeground(dpy,gc,lines[pos].color);
      XDrawLine(dpy,w,gc,lines[pos].x1,lines[pos].y1,lines[pos].x2,\
        lines[pos].y2);  // erase by xor
    }
    // otherwise calc a new one and draw it
    lines[pos].x1 = myrand() * WWIDTH;
    lines[pos].y1 = myrand() * WHEIGHT;
    lines[pos].x2 = myrand() * WWIDTH;
    lines[pos].y2 = myrand() * WHEIGHT;
    lines[pos].color = myrand() * 0xFFFFFF; // like HTML colors
    XSetForeground(dpy,gc,lines[pos].color);  // like pen color
    XDrawLine(dpy,w,gc,lines[pos].x1,lines[pos].y1,lines[pos].x2,lines[pos].y2);  
    XFlush(dpy);
    pos++;
    if (pos > NLINES)
      pos = 0;  // make it a circular array
    usleep(50000);  // 50 milliseconds per line
  }
}

int main(void) {
  srandom((unsigned int) time(NULL)); // seed random from time
  dpy = XOpenDisplay(NULL);
  assert(dpy);  // be sure we've got dpy
  blackcolor = BlackPixel(dpy, DefaultScreen(dpy));
  whitecolor = WhitePixel(dpy, DefaultScreen(dpy));
  //  By default the background of the new window is whatever was under it
  setwinattr.background_pixel = blackcolor;   // wnitecolor works too
  w = XCreateWindow(dpy,DefaultRootWindow(dpy),0,0,WWIDTH,WHEIGHT,0,24,\
    InputOutput,DefaultVisual(dpy,DefaultScreen(dpy)),CWBackPixel,&setwinattr);
  XSelectInput(dpy,w,StructureNotifyMask); // watching for MapNotify later
  XMapWindow(dpy,w); 
  gc = XCreateGC(dpy,w,0,NULL);
  XSetFunction(dpy,gc,GXxor); // sets xor mode
  // MapNotify wait loop, wait for a MapNotify event before drawing
  // confirms that XMapWindow worked. Could also wait for an Expose?
  for (;;) {
    XNextEvent(dpy,&e);  // fetch an event
    if (e.type == MapNotify)  // done when we find one
      break;
  }
  randlines();  // replace with your routine
  return 0;
}

User avatar
Paeryn
Posts: 2750
Joined: Wed Nov 23, 2011 1:10 am
Location: Sheffield, England

Re: Another silly xlib demo program

Thu Feb 16, 2017 9:03 pm

ab1jx wrote:But some things are broken now and not maintained, like I spent half a day trying to get XSetArcMode() to work and gave up. I wanted Pacman. :)
XSetArcMode works perfectly fine for me. It only affects XFillArc() not XDrawArc() if that was confusing you.
Modified version of your lines program:

Code: Select all

/*
  My second recreational X activity. Based on Tronche's tutorial.
  Copyright (c) 2017, Alan Corey, ab1jx, public domain
*/

#include <X11/Xlib.h> // Every Xlib program must include this
#include <assert.h>   // I include this to test return values the lazy way
#include <unistd.h>   // So we got the profile for 10 seconds
#include <stdio.h> 
#include <stdlib.h> 
#include <stdint.h> // for int16_t, int32_t, unambiguous
#include <time.h>  // time_t to seed random()

#define WWIDTH 320
#define WHEIGHT 200
#define NLINES 50  // size of lines[] array

Display *dpy;  // *struct
int blackcolor,whitecolor;
Window w;  // ID #
GC gc;
XEvent e; // a struct
XSetWindowAttributes setwinattr; // another struct

// to xor the lines for removing, we keep coords and color for each
struct arcrec {
   XArc arc;
   int mode;
   uint32_t color;
  };

struct arcrec arcs[NLINES];

double myrand(void) {  // convenience: BASICish  0..1 float random
  return ((random() * 1.0) / (RAND_MAX * 1.0));
}

void randarcs(void) {   // payload
  int pos = 0; // position in lines[] array
  for (pos=0; pos<NLINES; pos++) {  // init colors to 0 as unused flag
    arcs[pos].color = 0;
  }
  while (1) {
    if (arcs[pos].color != 0) { // old line exists
      XSetForeground(dpy,gc,arcs[pos].color);
      XSetArcMode(dpy,gc,arcs[pos].mode);
      XFillArcs(dpy,w,gc,&arcs[pos].arc,1);  // erase by xor
    }
    // otherwise calc a new one and draw it
    arcs[pos].arc.x = myrand() * WWIDTH;
    arcs[pos].arc.y = myrand() * WHEIGHT;
    arcs[pos].arc.width = myrand() * WWIDTH;
    arcs[pos].arc.height = myrand() * WHEIGHT;
    arcs[pos].arc.angle1 = myrand() * 64; // 64 units per degree
    arcs[pos].arc.angle2 = myrand() * 360 * 64;
    arcs[pos].color = myrand() * 0xFFFFFF; // like HTML colors
    XSetForeground(dpy,gc,arcs[pos].color);  // like pen color
    arcs[pos].mode = myrand() < 0.5 ? ArcPieSlice : ArcChord;
    XSetArcMode(dpy,gc,arcs[pos].mode);
    XFillArcs(dpy,w,gc,&arcs[pos].arc,1);
    XFlush(dpy);
    pos++;
    if (pos > NLINES)
      pos = 0;  // make it a circular array
    usleep(500000);  // 50 milliseconds per line
  }
}
Screenshot was taken on my PC for ease but it runs exactly the same on the RPi.
xarcs.PNG
arcs example
xarcs.PNG (10.01 KiB) Viewed 1607 times
She who travels light — forgot something.

User avatar
ab1jx
Posts: 868
Joined: Thu Sep 26, 2013 1:54 pm
Location: Heath, MA USA
Contact: Website

Re: Another silly xlib demo program

Sat Feb 18, 2017 3:43 am

Interesting, I was trying to do
XSetArcMode(dpy,gc,ArcPieSlice);
and
XFillArc(dpy,w,gc,20,12,80,80,0*64,90*64);

I wasn't using XArc, also I was only using the singular form. I was only getting chords, not pie slices. And that was with XDrawArc, not XFillArc. I was getting a non-zero return from XSetArcMode and segfaults from XFillArc. Working mostly from an old (1990) version of vol1.pdf

I wanted an object with colors to xor to erase and bounce around the screen. Your version works here. Heavy concentration of arcs at the lower right, maybe calc width & height first then subtract that from x and y.
arcs.png
xwd screenshot
arcs.png (41.65 KiB) Viewed 1471 times
Last edited by ab1jx on Sat Feb 18, 2017 4:54 am, edited 1 time in total.

User avatar
Paeryn
Posts: 2750
Joined: Wed Nov 23, 2011 1:10 am
Location: Sheffield, England

Re: Another silly xlib demo program

Sat Feb 18, 2017 4:29 am

ab1jx wrote:Interesting, I was trying to do
XSetArcMode(dpy,gc,ArcPieSlice);
and
XFillArc(dpy,w,gc,20,12,80,80,0*64,90*64);

I wasn't using XArc, also I was only using the singular form. I was only getting chords, not pie slices. And that was with XDrawArc, not XFillArc. I was getting a non-zero return from XSetArcMode and segfaults from XFillArc. Working mostly from an old (1990) version of vol1.pdf

I wanted an object with colors to xor to erase and bounce around the screen. Your version works here.
XDrawArc only draws the arc itself, it never closes the two ends together either with a chord or a pie segment. XFillArcs is basically the same as XFillArc but easier as you only need to pass a pointer to an XArc struct rather than pass all the elements individually.

I just tried it using XFillArc like you showed and it still worked. Which error were you getting back from XSetArcMode? If you can post a small non-working example I'll look over it for you.
She who travels light — forgot something.

User avatar
ab1jx
Posts: 868
Joined: Thu Sep 26, 2013 1:54 pm
Location: Heath, MA USA
Contact: Website

Re: Another silly xlib demo program

Sat Feb 18, 2017 5:06 am

Naw, I'd have to make one. I have some versioning but arc became lines so that's gone.

I'm trying this to shift the bulk of them toward the center:
arcs[pos].arc.width = myrand() * WWIDTH;
arcs[pos].arc.height = myrand() * WHEIGHT;
arcs[pos].arc.x = (myrand() * WWIDTH) - arcs[pos].arc.width/2;
arcs[pos].arc.y = (myrand() * WHEIGHT) - arcs[pos].arc.height/2;

And I use this alias for screen shots:
alias snap='sleep 10 ; xwd -root -out /tmp/snap.xwd'
Then open /tmp/snap.xwd in gimp to edit and save.

The text I was studying shows this for modes:
arc_modes_old.png
arc_modes_old.png (5.68 KiB) Viewed 1443 times
Y'know if you set the window size to the screen size and have it stop on a keypress or mouse move event you'd have not a bad screensaver.

User avatar
ab1jx
Posts: 868
Joined: Thu Sep 26, 2013 1:54 pm
Location: Heath, MA USA
Contact: Website

Re: Another silly xlib demo program

Sun May 20, 2018 9:40 pm

Hint, replace WWIDTH and WHEIGHT with WidthOfScreen(scrn) and HeightOfScreen(scrn) after scrn is defined.

Return to “C/C++”