added: bitx11, a program that captures the X11 root window and write the output data to the bitlair led panel using Floy-Steinberg dithering
This commit is contained in:
parent
2b8a68b83f
commit
67bcbab47f
4 changed files with 448 additions and 0 deletions
319
src/bitx11/bitx11.cpp
Normal file
319
src/bitx11/bitx11.cpp
Normal file
|
@ -0,0 +1,319 @@
|
||||||
|
/*
|
||||||
|
* bitvis
|
||||||
|
* Copyright (C) Bob 2012
|
||||||
|
*
|
||||||
|
* bitvis is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* bitvis is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
* See the GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "bitx11.h"
|
||||||
|
#include "util/misc.h"
|
||||||
|
#include "util/log.h"
|
||||||
|
#include "util/timeutils.h"
|
||||||
|
#include "util/inclstdint.h"
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
CBitX11::CBitX11(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
m_port = 1337;
|
||||||
|
m_address = NULL;
|
||||||
|
m_fps = 30.0f;
|
||||||
|
m_destwidth = 120;
|
||||||
|
m_destheight = 48;
|
||||||
|
m_debug = false;
|
||||||
|
m_debugscale = 2;
|
||||||
|
|
||||||
|
const char* flags = "p:a:f:d:";
|
||||||
|
int c;
|
||||||
|
while ((c = getopt(argc, argv, flags)) != -1)
|
||||||
|
{
|
||||||
|
if (c == 'f') //fps
|
||||||
|
{
|
||||||
|
float fps;
|
||||||
|
if (!StrToFloat(string(optarg), fps) || fps <= 0)
|
||||||
|
{
|
||||||
|
LogError("Wrong argument \"%s\" for fps", optarg);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_fps = fps;
|
||||||
|
}
|
||||||
|
else if (c == 'p') //port
|
||||||
|
{
|
||||||
|
int port;
|
||||||
|
if (!StrToInt(string(optarg), port) || port < 0 || port > 65535)
|
||||||
|
{
|
||||||
|
LogError("Wrong argument \"%s\" for port", optarg);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_port = port;
|
||||||
|
}
|
||||||
|
else if (c == 'a') //address
|
||||||
|
{
|
||||||
|
m_address = optarg;
|
||||||
|
}
|
||||||
|
else if (c == 'd') //debug
|
||||||
|
{
|
||||||
|
m_debug = true;
|
||||||
|
|
||||||
|
int scale;
|
||||||
|
if (!StrToInt(string(optarg), scale) || scale <= 0)
|
||||||
|
{
|
||||||
|
LogError("Wrong argument \"%s\" for debug scale", optarg);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_debugscale = scale;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//if no address is specified, turn on the debug window instead
|
||||||
|
if (!m_address)
|
||||||
|
m_debug = true;
|
||||||
|
|
||||||
|
m_dpy = NULL;
|
||||||
|
m_srcformat = NULL;
|
||||||
|
m_dstformat = NULL;
|
||||||
|
m_pixmap = None;
|
||||||
|
m_srcpicture = None;
|
||||||
|
m_dstpicture = None;
|
||||||
|
|
||||||
|
memset(&m_pictattr, 0, sizeof(m_pictattr));
|
||||||
|
m_pictattr.repeat = RepeatNone;
|
||||||
|
|
||||||
|
memset(&m_transform, 0, sizeof(m_transform));
|
||||||
|
memset(&m_debugtransform, 0, sizeof(m_debugtransform));
|
||||||
|
}
|
||||||
|
|
||||||
|
CBitX11::~CBitX11()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void CBitX11::Setup()
|
||||||
|
{
|
||||||
|
m_dpy = XOpenDisplay(NULL);
|
||||||
|
if (m_dpy == NULL)
|
||||||
|
{
|
||||||
|
LogError("Unable to open display");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_rootwin = RootWindow(m_dpy, DefaultScreen(m_dpy));
|
||||||
|
XGetWindowAttributes(m_dpy, m_rootwin, &m_rootattr);
|
||||||
|
|
||||||
|
m_pixmap = XCreatePixmap(m_dpy, m_rootwin, m_destwidth, m_destheight, m_rootattr.depth);
|
||||||
|
|
||||||
|
m_srcformat = XRenderFindVisualFormat(m_dpy, m_rootattr.visual);
|
||||||
|
m_dstformat = XRenderFindVisualFormat(m_dpy, m_rootattr.visual);
|
||||||
|
m_srcpicture = XRenderCreatePicture(m_dpy, m_rootwin, m_srcformat, CPRepeat, &m_pictattr);
|
||||||
|
m_dstpicture = XRenderCreatePicture(m_dpy, m_pixmap, m_dstformat, CPRepeat, &m_pictattr);
|
||||||
|
|
||||||
|
XRenderSetPictureFilter(m_dpy, m_srcpicture, "bilinear", NULL, 0);
|
||||||
|
|
||||||
|
m_xim = XShmCreateImage(m_dpy, m_rootattr.visual, m_rootattr.depth, ZPixmap, NULL, &m_shmseginfo, m_destwidth, m_destheight);
|
||||||
|
m_shmseginfo.shmid = shmget(IPC_PRIVATE, m_xim->bytes_per_line * m_xim->height, IPC_CREAT | 0777);
|
||||||
|
m_shmseginfo.shmaddr = reinterpret_cast<char*>(shmat(m_shmseginfo.shmid, NULL, 0));
|
||||||
|
m_xim->data = m_shmseginfo.shmaddr;
|
||||||
|
m_shmseginfo.readOnly = False;
|
||||||
|
XShmAttach(m_dpy, &m_shmseginfo);
|
||||||
|
|
||||||
|
if (m_debug)
|
||||||
|
{
|
||||||
|
m_debugwindow = XCreateSimpleWindow(m_dpy, RootWindow(m_dpy, DefaultScreen(m_dpy)),
|
||||||
|
0, 0, m_destwidth * m_debugscale, m_destheight * m_debugscale, 0, 0, 0);
|
||||||
|
XMapWindow(m_dpy, m_debugwindow);
|
||||||
|
XFlush(m_dpy);
|
||||||
|
|
||||||
|
m_debuggc = XCreateGC(m_dpy, m_debugwindow, 0, NULL);
|
||||||
|
m_debugpixmap = XCreatePixmap(m_dpy, m_rootwin, m_destwidth, m_destheight, m_rootattr.depth);
|
||||||
|
m_debugsrcformat = XRenderFindVisualFormat(m_dpy, m_rootattr.visual);
|
||||||
|
m_debugdstformat = XRenderFindVisualFormat(m_dpy, m_rootattr.visual);
|
||||||
|
m_debugsrcpicture = XRenderCreatePicture(m_dpy, m_debugpixmap, m_debugsrcformat, CPRepeat, &m_pictattr);
|
||||||
|
m_debugdstpicture = XRenderCreatePicture(m_dpy, m_debugwindow, m_debugdstformat, CPRepeat, &m_pictattr);
|
||||||
|
XRenderSetPictureFilter(m_dpy, m_debugsrcpicture, "nearest", NULL, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CBitX11::Process()
|
||||||
|
{
|
||||||
|
int64_t looptime = GetTimeUs();
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
if (!m_socket.IsOpen() && m_address)
|
||||||
|
{
|
||||||
|
if (m_socket.Open(m_address, m_port, 1000000) != SUCCESS)
|
||||||
|
{
|
||||||
|
LogError("Failed to connect: %s", m_socket.GetError().c_str());
|
||||||
|
m_socket.Close();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Log("Connected");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_transform.matrix[0][0] = m_rootattr.width;
|
||||||
|
m_transform.matrix[1][1] = m_rootattr.width;
|
||||||
|
m_transform.matrix[2][2] = m_destwidth;
|
||||||
|
|
||||||
|
//the aspect ratio of the root window is probably different from the target aspect ratio
|
||||||
|
//so clip parts from the top and bottom of the root window
|
||||||
|
int offset = (m_rootattr.height * m_destwidth / m_rootattr.width - m_destheight) / 2;
|
||||||
|
|
||||||
|
//render the root window to m_pixmap using xrender
|
||||||
|
XRenderSetPictureTransform (m_dpy, m_srcpicture, &m_transform);
|
||||||
|
XRenderComposite(m_dpy, PictOpSrc, m_srcpicture, None, m_dstpicture, 0, offset, 0, 0, 0, 0, m_destwidth, m_destheight);
|
||||||
|
XShmGetImage(m_dpy, m_pixmap, m_xim, 0, 0, AllPlanes);
|
||||||
|
|
||||||
|
//allocate 2 extra pixels on each line, and allocate one extra line
|
||||||
|
//since the Floy-Steinberg dithering writes to one line below the current pixel
|
||||||
|
//and writes to one pixel to the left and right of the current pixel
|
||||||
|
int planewidth = m_destwidth + 2;
|
||||||
|
int planeheight = m_destheight + 1;
|
||||||
|
int planes[2][planewidth * planeheight];
|
||||||
|
int avg = 0;
|
||||||
|
//copy the red and green pixels to the planes, and calculate the average pixel value
|
||||||
|
for (int y = 0; y < m_destheight; y++)
|
||||||
|
{
|
||||||
|
uint8_t* ximline = (uint8_t*)m_xim->data + y * m_xim->bytes_per_line;
|
||||||
|
uint8_t* ximend = ximline + m_xim->bytes_per_line;
|
||||||
|
int* planeliner = planes[0] + y * planewidth + 1;
|
||||||
|
int* planelineg = planes[1] + y * planewidth + 1;
|
||||||
|
|
||||||
|
while (ximline != ximend)
|
||||||
|
{
|
||||||
|
ximline++;
|
||||||
|
avg += *(planelineg++) = *(ximline++);
|
||||||
|
avg += *(planeliner++) = *(ximline++);
|
||||||
|
ximline++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
avg /= m_destwidth * m_destheight * 2;
|
||||||
|
|
||||||
|
//quantize the planes, apply Floy-Steinberg dithering, and write into the buffer for the socket
|
||||||
|
CTcpData data;
|
||||||
|
data.SetData(":00");
|
||||||
|
for (int y = 0; y < m_destheight; y++)
|
||||||
|
{
|
||||||
|
uint8_t ledline[m_destwidth / 4];
|
||||||
|
memset(ledline, 0, sizeof(ledline));
|
||||||
|
|
||||||
|
for (int i = 0; i < 2; i++)
|
||||||
|
{
|
||||||
|
int* line = planes[i] + y * planewidth + 1;
|
||||||
|
int* lineend = line + m_destwidth;
|
||||||
|
int quantval;
|
||||||
|
int quanterror;
|
||||||
|
|
||||||
|
uint8_t* ledpos = ledline;
|
||||||
|
int ledcounter = 0;
|
||||||
|
|
||||||
|
while (line != lineend)
|
||||||
|
{
|
||||||
|
//if a pixel is higher than the average, set it to the max value
|
||||||
|
//this will turn on the led
|
||||||
|
if (*line > avg)
|
||||||
|
{
|
||||||
|
quantval = 255;
|
||||||
|
*ledpos |= 1 << ((ledcounter * 2) + (1 - i));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
quantval = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ledcounter++;
|
||||||
|
if (ledcounter == 4)
|
||||||
|
{
|
||||||
|
ledcounter = 0;
|
||||||
|
ledpos++;
|
||||||
|
}
|
||||||
|
|
||||||
|
//apply Floyd-Steinberg dither
|
||||||
|
quanterror = *line - quantval;
|
||||||
|
*line = quantval;
|
||||||
|
|
||||||
|
line[1] += quanterror * 7 / 16;
|
||||||
|
line[m_destwidth - 1] += quanterror * 3 / 16;
|
||||||
|
line[m_destwidth] += quanterror * 5 / 16;
|
||||||
|
line[m_destwidth + 1] += quanterror / 16;
|
||||||
|
|
||||||
|
line++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//append the line to the buffer
|
||||||
|
data.SetData(ledline, sizeof(ledline), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
//add 10 zeros to the buffer in case the receiver is out of sync
|
||||||
|
uint8_t end[10] = {};
|
||||||
|
data.SetData(end, sizeof(end), true);
|
||||||
|
|
||||||
|
if (m_socket.IsOpen())
|
||||||
|
{
|
||||||
|
if (m_socket.Write(data) != SUCCESS)
|
||||||
|
{
|
||||||
|
LogError("%s", m_socket.GetError().c_str());
|
||||||
|
m_socket.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_debug)
|
||||||
|
{
|
||||||
|
//write the quantized and dithered planes back into the Ximage
|
||||||
|
for (int y = 0; y < m_destheight; y++)
|
||||||
|
{
|
||||||
|
uint8_t* ximline = (uint8_t*)m_xim->data + y * m_xim->bytes_per_line;
|
||||||
|
uint8_t* ximend = ximline + m_xim->bytes_per_line;
|
||||||
|
int* planeliner = planes[0] + y * planewidth + 1;
|
||||||
|
int* planelineg = planes[1] + y * planewidth + 1;
|
||||||
|
|
||||||
|
while (ximline != ximend)
|
||||||
|
{
|
||||||
|
*(ximline++) = 0;
|
||||||
|
*(ximline++) = Clamp(*(planelineg++), 0, 255);
|
||||||
|
*(ximline++) = Clamp(*(planeliner++), 0, 255);
|
||||||
|
ximline++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//write the Ximage back into the pixmap
|
||||||
|
XShmPutImage(m_dpy, m_debugpixmap, m_debuggc, m_xim, 0, 0, 0, 0, m_destwidth, m_destheight, False);
|
||||||
|
|
||||||
|
m_debugtransform.matrix[0][0] = m_destwidth;
|
||||||
|
m_debugtransform.matrix[1][1] = m_destwidth;
|
||||||
|
m_debugtransform.matrix[2][2] = m_destwidth * m_debugscale;
|
||||||
|
|
||||||
|
//render the pixmap on the debug window, scaled by m_debugscale
|
||||||
|
XRenderSetPictureTransform (m_dpy, m_debugsrcpicture, &m_debugtransform);
|
||||||
|
XRenderComposite(m_dpy, PictOpSrc, m_debugsrcpicture, None, m_debugdstpicture,
|
||||||
|
0, 0, 0, 0, 0, 0, m_destwidth * m_debugscale, m_destheight * m_debugscale);
|
||||||
|
|
||||||
|
XFlush(m_dpy);
|
||||||
|
}
|
||||||
|
|
||||||
|
looptime += Round64(1000000.0f / m_fps);
|
||||||
|
USleep(looptime - GetTimeUs());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CBitX11::Cleanup()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
77
src/bitx11/bitx11.h
Normal file
77
src/bitx11/bitx11.h
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
/*
|
||||||
|
* bitvis
|
||||||
|
* Copyright (C) Bob 2012
|
||||||
|
*
|
||||||
|
* bitvis is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* bitvis is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
* See the GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BITX11_H
|
||||||
|
#define BITX11_H
|
||||||
|
|
||||||
|
#include "util/tcpsocket.h"
|
||||||
|
|
||||||
|
#include <X11/Xlib.h>
|
||||||
|
#include <X11/extensions/Xrender.h>
|
||||||
|
#include <X11/extensions/XShm.h>
|
||||||
|
#include <sys/ipc.h>
|
||||||
|
#include <sys/shm.h>
|
||||||
|
|
||||||
|
class CBitX11
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CBitX11(int argc, char *argv[]);
|
||||||
|
~CBitX11();
|
||||||
|
|
||||||
|
void Setup();
|
||||||
|
void Process();
|
||||||
|
void Cleanup();
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
int m_port;
|
||||||
|
const char* m_address;
|
||||||
|
float m_fps;
|
||||||
|
|
||||||
|
CTcpClientSocket m_socket;
|
||||||
|
|
||||||
|
Display* m_dpy;
|
||||||
|
Window m_rootwin;
|
||||||
|
XWindowAttributes m_rootattr;
|
||||||
|
|
||||||
|
int m_destwidth;
|
||||||
|
int m_destheight;
|
||||||
|
|
||||||
|
Pixmap m_pixmap;
|
||||||
|
XRenderPictFormat* m_srcformat;
|
||||||
|
XRenderPictFormat* m_dstformat;
|
||||||
|
Picture m_srcpicture;
|
||||||
|
Picture m_dstpicture;
|
||||||
|
XRenderPictureAttributes m_pictattr;
|
||||||
|
XTransform m_transform;
|
||||||
|
XShmSegmentInfo m_shmseginfo;
|
||||||
|
XImage* m_xim;
|
||||||
|
|
||||||
|
bool m_debug;
|
||||||
|
int m_debugscale;
|
||||||
|
Window m_debugwindow;
|
||||||
|
GC m_debuggc;
|
||||||
|
Pixmap m_debugpixmap;
|
||||||
|
XRenderPictFormat* m_debugsrcformat;
|
||||||
|
XRenderPictFormat* m_debugdstformat;
|
||||||
|
Picture m_debugsrcpicture;
|
||||||
|
Picture m_debugdstpicture;
|
||||||
|
XTransform m_debugtransform;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //BITX11_H
|
31
src/bitx11/main.cpp
Normal file
31
src/bitx11/main.cpp
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
/*
|
||||||
|
* bitx11
|
||||||
|
* Copyright (C) Bob 2012
|
||||||
|
*
|
||||||
|
* bitx11 is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* bitx11 is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
* See the GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "bitx11.h"
|
||||||
|
|
||||||
|
int main (int argc, char *argv[])
|
||||||
|
{
|
||||||
|
CBitX11 bitx11(argc, argv);
|
||||||
|
|
||||||
|
bitx11.Setup();
|
||||||
|
bitx11.Process();
|
||||||
|
bitx11.Cleanup();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
21
wscript
21
wscript
|
@ -18,11 +18,20 @@ def configure(conf):
|
||||||
conf.check(header_name='jack/jack.h')
|
conf.check(header_name='jack/jack.h')
|
||||||
conf.check(header_name='fftw3.h')
|
conf.check(header_name='fftw3.h')
|
||||||
conf.check(header_name='samplerate.h')
|
conf.check(header_name='samplerate.h')
|
||||||
|
conf.check(header_name='sys/ipc.h')
|
||||||
|
conf.check(header_name='sys/shm.h')
|
||||||
|
|
||||||
|
conf.check(header_name='X11/Xlib.h', auto_add_header_name=True)
|
||||||
|
conf.check(header_name='X11/extensions/Xrender.h')
|
||||||
|
conf.check(header_name='X11/extensions/XShm.h')
|
||||||
|
|
||||||
conf.check(lib='fftw3', uselib_store='fftw3')
|
conf.check(lib='fftw3', uselib_store='fftw3')
|
||||||
conf.check(lib='fftw3f', uselib_store='fftw3f')
|
conf.check(lib='fftw3f', uselib_store='fftw3f')
|
||||||
conf.check(lib='jack', uselib_store='jack')
|
conf.check(lib='jack', uselib_store='jack')
|
||||||
conf.check(lib='samplerate', uselib_store='samplerate')
|
conf.check(lib='samplerate', uselib_store='samplerate')
|
||||||
|
conf.check(lib='X11', uselib_store='X11')
|
||||||
|
conf.check(lib='Xext', uselib_store='Xext')
|
||||||
|
conf.check(lib='Xrender', uselib_store='Xrender')
|
||||||
conf.check(lib='m', uselib_store='m', mandatory=False)
|
conf.check(lib='m', uselib_store='m', mandatory=False)
|
||||||
conf.check(lib='pthread', uselib_store='pthread', mandatory=False)
|
conf.check(lib='pthread', uselib_store='pthread', mandatory=False)
|
||||||
|
|
||||||
|
@ -50,3 +59,15 @@ def build(bld):
|
||||||
cxxflags='-Wall -g -DUTILNAMESPACE=BitVisUtil',
|
cxxflags='-Wall -g -DUTILNAMESPACE=BitVisUtil',
|
||||||
target='bitvis')
|
target='bitvis')
|
||||||
|
|
||||||
|
bld.program(source='src/bitx11/main.cpp\
|
||||||
|
src/bitx11/bitx11.cpp\
|
||||||
|
src/util/log.cpp\
|
||||||
|
src/util/misc.cpp\
|
||||||
|
src/util/mutex.cpp\
|
||||||
|
src/util/timeutils.cpp\
|
||||||
|
src/util/tcpsocket.cpp',
|
||||||
|
use=['m', 'rt', 'X11', 'Xrender', 'Xext'],
|
||||||
|
includes='./src',
|
||||||
|
cxxflags='-Wall -g -DUTILNAMESPACE=BitX11Util',
|
||||||
|
target='bitx11')
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue