Blob Blame History Raw

#include "xmain.h"


Display *dpy;
int screen;
Window win;
Drawable drw;
Visual *visual;
GC gc;
int winW, winH;

static int dblbuf;

int run;
int exitCode;


void stop(int code) {
  if (!exitCode)
    exitCode = code;
  LOGDBG("stop: code %d (%d)", code, exitCode);
  run = 0;
}


int main() {
  LOGDBG("main: open dysplay");
  dpy = XOpenDisplay(NULL);
  if (!dpy)
    return LOGERR("init: cannot connect to xcb"), 1;

  LOGDBG("main: get screen");
  screen = DefaultScreen(dpy);

  LOGDBG("main: search 24 bit visual");
  XVisualInfo vi = {};
  if (!XMatchVisualInfo(dpy, screen, 24, TrueColor, &vi))
    return LOGERR("main: cannot get 24 bit visual"), XCloseDisplay(dpy), 1;

  LOGDBG("main: query double buffering extension");
  int major, minor;
  if (XdbeQueryExtension(dpy, &major, &minor)) {
    LOGDBG("main: XdbeGetVisualInfo");
    int numScreens = 1;
    Drawable roots[] = { DefaultRootWindow(dpy) };
    XdbeScreenVisualInfo *info = XdbeGetVisualInfo(dpy, roots, &numScreens);
    if (info && numScreens > 0 && info->count > 0) {
      XVisualInfo visinfo = {
        .visualid = info->visinfo[0].visual,
        .screen = screen,
        .depth = 24 };

      LOGDBG("main: XGetVisualInfo for double buffering");
      int matches;
      XVisualInfo *match = XGetVisualInfo(dpy, VisualIDMask | VisualScreenMask | VisualDepthMask, &visinfo, &matches);
      if (match && matches > 0) {
        LOGDBG("main: double buffering is supported");
        dblbuf = 1;
        vi = *match;
      }
    }
  }

  LOGDBG("main: get root window");
  Window root = RootWindow(dpy, screen);

  LOGDBG("main: create window");
  XSetWindowAttributes attr = {};
  attr.colormap = XCreateColormap(dpy, XDefaultRootWindow(dpy), vi.visual, AllocNone);
  drw = win = XCreateWindow(
    dpy, root,           // display and parent window
    10, 10, 512, 512, 0, // position, size and border width
    24,                  // depth
    CopyFromParent,//InputOutput,         // class
    vi.visual,           // visual
    0,//CWBackPixel | CWColormap | CWBorderPixel, // attributes mask
    &attr );             // attributes

  if (dblbuf) {
    LOGDBG("main: allocate back buffer");
    drw = XdbeAllocateBackBufferName(dpy, win, XdbeUndefined);
    if (!drw) {
      LOGWRN("main: cannot allocate backbuffer");
      drw = win;
    }
  }

  LOGDBG("main: select input");
  XSelectInput(
    dpy, win,
      StructureNotifyMask
    | ExposureMask
    | ButtonPressMask
    | ButtonReleaseMask );

  LOGDBG("main: subscribe to windows delete message from window manager");
  Atom aWmDel = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
  XSetWMProtocols(dpy, win, &aWmDel, 1);

  LOGDBG("main: map window");
  XMapWindow(dpy, win);

  LOGDBG("main: get window attributes");
  XWindowAttributes wa = {};
  if (!XGetWindowAttributes(dpy, win, &wa))
    return LOGERR("main: cannot get window attributes"), XCloseDisplay(dpy), 1;
  visual = wa.visual;
  winW = wa.width;
  winH = wa.height;

  LOGDBG("main: greate gc");
  gc = XCreateGC(dpy, drw, 0, NULL);

  XFlush(dpy);

  LOGDBG("main: init");
  run = 1;
  if (!init())
    stop(1);

  LOGDBG("main: start event loop");
  XEvent event = {};
  int redrawRequested = 0;
  while(run) {
    XNextEvent(dpy, &event);
    int forceDraw = 0;
    switch(event.type) {
    case Expose:
      if (!event.xexpose.count) {
        LOGDBG( "main: event: expose: x=%d, y=%d, w=%d, h=%d",
                event.xexpose.x, event.xexpose.y, event.xexpose.width, event.xexpose.height );
        redrawRequested = 1;
      }
      break;
    case ConfigureNotify:
      if ( event.xconfigure.width
        && event.xconfigure.height
        && ( winW != event.xconfigure.width
          || winH != event.xconfigure.height ))
      {
        LOGDBG( "main: event: moved: x=%d y=%d w=%d, h=%d",
                event.xconfigure.x, event.xconfigure.y, event.xconfigure.width, event.xconfigure.height );
        winW = event.xconfigure.width;
        winH = event.xconfigure.height;
        resize();
        redrawRequested = 1;
      }
      break;
    case ButtonPress:
      if (event.xbutton.button == 1) {
        LOGDBG("main: event: mouse down: x=%d, y=%d", event.xbutton.x, event.xbutton.y);
        mouseDown(event.xbutton.x, event.xbutton.y);
        forceDraw = 1;
      }
      break;
    case ButtonRelease:
      if (event.xbutton.button == 1) {
        LOGDBG("main: event: mouse up: x=%d, y=%d", event.xbutton.x, event.xbutton.y);
        mouseUp();
        forceDraw = 1;
      }
      break;
    case ClientMessage:
      if (event.xclient.data.l[0] == aWmDel) {
        LOGDBG("main: event: delete window event");
        stop(0);
      }
      break;
    }

    if (forceDraw || (redrawRequested && !XEventsQueued(dpy, QueuedAfterFlush))) {
      if (winW > 0 && winH > 0) {
        LOGDBG("main: draw");
        draw();
        if (dblbuf) {
          XdbeSwapInfo swapInfo = {
            .swap_window = win,
            .swap_action = XdbeUndefined };
          if (!XdbeSwapBuffers(dpy, &swapInfo, 1))
            LOGWRN("main: cannot swap buffers");
        }
        XFlush(dpy);
      }
      redrawRequested = 0;
    }
  }

  XFlush(dpy);

  LOGDBG("main: deinit");
  deinit();

  LOGDBG("main: close display");
  XCloseDisplay(dpy);
  return exitCode;
}