crt/xwin.cpp
#include "xwin.hpp"
#include <linux/limits.h>
#ifdef USE_X
XImg::XImg(Display* display, Visual* visual, unsigned w, unsigned h)
: buf((Img::rgb_t*)calloc(w*h, sizeof(Img::rgb_t))), img(new Img(w, h, buf, NULL)) {
gctx = DefaultGC(display, DefaultScreen(display)); // NOLINT(clang-analyzer-core.NullDereference)
xim = XCreateImage(display, visual, 24, ZPixmap, 0, (char*)buf, w, h, sizeof(Img::rgb_t)*8, w*sizeof(Img::rgb_t));
}
XImg::~XImg() {
if (xim) {
XDestroyImage(xim); // also frees buf
} else {
free(buf);
}
delete img;
}
Img* XImg::get() {
return img;
}
bool XImg::draw(Display* display, Window window) {
XPutImage(display, window, gctx, xim, 0, 0, 0, 0, img->w, img->h); // TODO: MIT-SHM for performace reasons
return true;
}
XWin::XWin(unsigned w, unsigned h) {
display = XOpenDisplay(NULL);
if (!display) {
return;
}
visual = DefaultVisual(display, 0);
if (visual->c_class != TrueColor) {
XCloseDisplay(display);
display = NULL;
}
window = XCreateSimpleWindow(display, RootWindow(display, 0), 0, 0, w, h, 1, 0, 0); // NOLINT(clang-analyzer-core.NullDereference)
if (!window) {
XCloseDisplay(display);
display = NULL;
}
if (!XSelectInput(display, window, KeyPressMask) || !XMapWindow(display, window)) {
XDestroyWindow(display, window);
XCloseDisplay(display);
display = NULL;
}
ximg = new XImg(display, visual, w, h);
}
XWin::~XWin() {
if (!display) return;
delete ximg;
XDestroyWindow(display, window);
XCloseDisplay(display);
}
Img* XWin::get() {
return display? ximg->get(): NULL;
}
bool XWin::draw() {
return (display && ximg->draw(display, window));
}
XWin::key_t XWin::get_key() {
XEvent ev;
#ifndef KEY_REPEAT
while (XPending(display)) XNextEvent(display, &ev);
#endif
while (true) {
XNextEvent(display, &ev);
if (ev.type != KeyPress) continue;
KeySym keysym = XLookupKeysym(&ev.xkey, 0);
if (keysym == NoSymbol) return KEY_QUIT;
XKeyEvent* kev = (XKeyEvent*)&ev;
switch (keysym) {
case XK_Left:
return (kev->state & Mod1Mask)? KEY_PAN_LEFT: KEY_LEFT;
case XK_Up:
return (kev->state & Mod1Mask)? KEY_PAN_DOWN: KEY_FORWARD;
case XK_Right:
return (kev->state & Mod1Mask)? KEY_PAN_RIGHT: KEY_RIGHT;
case XK_Down:
return (kev->state & Mod1Mask)? KEY_PAN_UP: KEY_BACK;
case XK_Page_Up:
return KEY_UP;
case XK_Page_Down:
return KEY_DOWN;
case XK_Escape:
return KEY_QUIT;
default:
break; // ignore
}
}
return KEY_QUIT; // not reached
}
#endif
OutImg::OutImg(unsigned ww, unsigned hh, unsigned samp) {
if (samp) {
if (samp <= 1 || ww % samp != 0 || hh % samp != 0 || samp > ww || samp > hh) {
LOG("supersampling value %u invalid, skipping.", samp);
samp = 0;
}
}
supersample = samp;
#ifdef USE_X
if (samp) {
xwin = new XWin(ww / samp, hh / samp);
if (xwin->get()) {
out = new Img(ww, hh);
out_sampled = xwin->get();
return;
} else {
delete xwin;
xwin = NULL;
}
} else {
xwin = new XWin(ww, hh);
if (xwin->get()) {
out = xwin->get();
out_sampled = NULL;
return;
} else {
delete xwin;
xwin = NULL;
}
}
LOG("not using X window");
#endif
if (samp) {
out = new Img(ww, hh);
out_sampled = new Img(ww / samp, hh / samp);
} else {
out = new Img(ww, hh);
out_sampled = NULL;
}
}
bool OutImg::has_x() const {
#ifdef USE_X
return xwin != NULL;
#else
return false;
#endif
}
OutImg::~OutImg() {
#ifdef USE_X
if (xwin) {
delete xwin;
if (supersample) delete out;
return;
}
#endif
delete out;
if (supersample) delete out_sampled;
}
bool OutImg::put(const char* outdir) {
if (supersample) {
if (!out->supersample(supersample, out_sampled)) { // TODO: detach or in thread?
return false;
}
}
#ifdef USE_X
if (xwin && !xwin->draw()) {
return false;
}
#endif
if (outdir && *outdir) {
static unsigned frame_no = 0;
static char outname[PATH_MAX+1];
snprintf(outname, sizeof(outname), "%s/%u.ppm", outdir, frame_no++);
if (!(supersample? out_sampled->to_ppm(outname): out->to_ppm(outname))) { // TODO: detach? (blocking i/o)
return false;
}
}
return true;
}
bool OutImg::get_input(coord_t msens, coord_t lsens, vertex_t& move, vertex_t& look) {
msens = MAX(msens, 1.0);
lsens = MAX(lsens, 1.0);
move = vertex_t(0, 0, 0);
look = vertex_t(0, 0, 0);
#ifdef USE_X
if (!xwin) return false;
switch (xwin->get_key()) {
case XWin::KEY_UP:
move.y = -msens; return true;
case XWin::KEY_DOWN:
move.y = msens; return true;
case XWin::KEY_LEFT:
move.x = -msens; return true;
case XWin::KEY_RIGHT:
move.x = msens; return true;
case XWin::KEY_FORWARD:
move.z = msens; return true;
case XWin::KEY_BACK:
move.z = -msens; return true;
case XWin::KEY_PAN_UP:
look.y = lsens; return true;
case XWin::KEY_PAN_DOWN:
look.y = -lsens; return true;
case XWin::KEY_PAN_LEFT:
look.x = -lsens; return true;
case XWin::KEY_PAN_RIGHT:
look.x = lsens; return true;
case XWin::KEY_QUIT:
default:
return false;
}
return true;
#else
return false;
#endif
}