Blame app.c

Ivan Mahonin 452870
Ivan Mahonin 452870
#include "app.h"
Ivan Mahonin 452870
Ivan Mahonin cbe4b8
#include <math.h>
Ivan Mahonin cbe4b8
Ivan Mahonin 5b6cfc
#include <sys/time.h>
Ivan Mahonin 5b6cfc
#include <sys/types.h>
Ivan Mahonin fd85b0
#include <poll.h>
Ivan Mahonin 5b6cfc
#include <unistd.h>
Ivan Mahonin 452870
Ivan Mahonin dbe1a1
#include <X11/Xutil.h>
Ivan Mahonin dbe1a1
#include <X11/Xatom.h>
Ivan Mahonin 452870
Ivan Mahonin 452870
Ivan Mahonin 7e0df9
int appInit(App *app, const char *touch_dev) {
Ivan Mahonin 452870
  LOGDBG("app: init");
Ivan Mahonin dbe1a1
  
Ivan Mahonin dbe1a1
  CLEARFROM(app, dpy);
Ivan Mahonin 452870
  
Ivan Mahonin 452870
  LOGDBG("app: init: connect to xcb");
Ivan Mahonin dbe1a1
  app->dpy = XOpenDisplay(NULL);
Ivan Mahonin dbe1a1
  if (!app->dpy)
Ivan Mahonin 452870
    return LOGERR("app: init: cannot connect to xcb");
Ivan Mahonin 452870
  
Ivan Mahonin 452870
  LOGDBG("app: init: get screen");
Ivan Mahonin dbe1a1
  app->screen = DefaultScreen(app->dpy);
Ivan Mahonin dbe1a1
  app->sw = DisplayWidth(app->dpy, app->screen);
Ivan Mahonin dbe1a1
  app->sh = DisplayHeight(app->dpy, app->screen);
Ivan Mahonin 7e0df9
  XRRRotations(app->dpy, app->screen, &app->sr);
Ivan Mahonin 7e0df9
  LOGDBG("app: init: screen=%d sw=%d sh=%d sr=%02x", app->screen, app->sw, app->sh, app->sr);
Ivan Mahonin dbe1a1
  
Ivan Mahonin dbe1a1
  LOGDBG("app: init: get root window");
Ivan Mahonin dbe1a1
  app->root = RootWindow(app->dpy, app->screen);
Ivan Mahonin dbe1a1
  
Ivan Mahonin dbe1a1
  LOGDBG("app: init: create window");
Ivan Mahonin dbe1a1
  
Ivan Mahonin dbe1a1
  app->x = 0;
Ivan Mahonin 4b2399
  app->w = app->sw * WIDTH_SCALE;
Ivan Mahonin 4b2399
  app->h = app->sh * HEIGHT_SCALE;
Ivan Mahonin cbe4b8
Ivan Mahonin cbe4b8
  #if defined(WIDTH_SCALE_P) && defined(HEIGHT_SCALE_P)
Ivan Mahonin cbe4b8
  if (app->sw < app->sh) {
Ivan Mahonin 56756f
    app->w = app->sw * WIDTH_SCALE_P;
Ivan Mahonin 56756f
    app->h = app->sh * HEIGHT_SCALE_P;
Ivan Mahonin cbe4b8
  }
Ivan Mahonin cbe4b8
  #endif
Ivan Mahonin cbe4b8
Ivan Mahonin 0da9df
  #ifdef TOP
Ivan Mahonin 0da9df
  app->y = 0;
Ivan Mahonin 0da9df
  #else
Ivan Mahonin dbe1a1
  app->y = app->sh - app->h;
Ivan Mahonin 0da9df
  #endif
Ivan Mahonin 7e0df9
Ivan Mahonin dbe1a1
  XSetWindowAttributes attr = {};
Ivan Mahonin dbe1a1
  #ifdef NOBORDER
Ivan Mahonin dbe1a1
  attr.override_redirect = 1;
Ivan Mahonin dbe1a1
  #endif
Ivan Mahonin dbe1a1
Ivan Mahonin dbe1a1
  app->win = XCreateWindow(
Ivan Mahonin dbe1a1
    app->dpy, app->root,               // display and parent window
Ivan Mahonin dbe1a1
    app->x, app->y, app->w, app->h, 0, // position, size and border width
Ivan Mahonin dbe1a1
    CopyFromParent, CopyFromParent, CopyFromParent, // depth, class and visual
Ivan Mahonin 51e530
    CWOverrideRedirect, &attr );        // mask with attributes
Ivan Mahonin dbe1a1
Ivan Mahonin dbe1a1
  XSelectInput(
Ivan Mahonin dbe1a1
    app->dpy, app->win,
Ivan Mahonin dbe1a1
      StructureNotifyMask
Ivan Mahonin dbe1a1
    | ExposureMask
Ivan Mahonin dbe1a1
    | ButtonPressMask
Ivan Mahonin dbe1a1
    | ButtonReleaseMask
Ivan Mahonin dbe1a1
    | Button1MotionMask );
Ivan Mahonin dbe1a1
Ivan Mahonin 51e530
  #if defined(NOBORDER) || defined(DOCK)
Ivan Mahonin 51e530
  LOGDBG("app: init: disable decorations");
Ivan Mahonin 51e530
  Atom amh = XInternAtom(app->dpy, "_MOTIF_WM_HINTS", False);
Ivan Mahonin 51e530
  long amhv[5] = { 2 };
Ivan Mahonin 51e530
  XChangeProperty(app->dpy, app->win, amh, amh, 32, PropModeReplace, (unsigned char*)amhv, 5);
Ivan Mahonin 51e530
  #endif
Ivan Mahonin 51e530
Ivan Mahonin dbe1a1
  #ifndef NOBORDER
Ivan Mahonin c456ae
  LOGDBG("app: init: set window title");
Ivan Mahonin dbe1a1
  char *title = TITLE;
Ivan Mahonin dbe1a1
  XTextProperty wtitle;
Ivan Mahonin dbe1a1
  if (!XStringListToTextProperty(&title, 1, &wtitle)) {
Ivan Mahonin dbe1a1
    LOGWRN("app: init: XStringListToTextProperty error for title: %s", title);
Ivan Mahonin dbe1a1
  } else {
Ivan Mahonin dbe1a1
    XSetWMName(app->dpy, app->win, &wtitle);
Ivan Mahonin dbe1a1
    XFree(wtitle.value);
Ivan Mahonin dbe1a1
  }
Ivan Mahonin 7aad5f
  #endif
Ivan Mahonin c456ae
Ivan Mahonin 51e530
  #ifdef DOCK
Ivan Mahonin c456ae
  LOGDBG("app: init: set window as dock");
Ivan Mahonin c456ae
  Atom awt = XInternAtom(app->dpy, "_NET_WM_WINDOW_TYPE", False);
Ivan Mahonin 451405
  Atom awtv = XInternAtom(app->dpy, DOCK_TYPE, False);
Ivan Mahonin 51e530
  XChangeProperty(app->dpy, app->win, awt, XA_ATOM, 32, PropModeReplace, (unsigned char*)&awtv, 1);
Ivan Mahonin 51e530
  Atom aaa = XInternAtom(app->dpy, "_NET_WM_ALLOWED_ACTIONS", False);
Ivan Mahonin 51e530
  XChangeProperty(app->dpy, app->win, aaa, XA_ATOM, 32, PropModeReplace, NULL, 0);
Ivan Mahonin 325e70
  Atom aws = XInternAtom(app->dpy, "_NET_WM_STATE", False);
Ivan Mahonin 325e70
  Atom awss = XInternAtom(app->dpy, "_NET_WM_STATE_STIKY", False);
Ivan Mahonin 325e70
  XChangeProperty(app->dpy, app->win, aws, XA_ATOM, 32, PropModeAppend, (unsigned char*)&awss, 1);
Ivan Mahonin 325e70
  Atom awd = XInternAtom(app->dpy, "_NET_WM_DESKTOP", False);
Ivan Mahonin 325e70
  long all = -1;
Ivan Mahonin 325e70
  XChangeProperty(app->dpy, app->win, awd, XA_ATOM, 32, PropModeReplace, (unsigned char*)&all, 1);
Ivan Mahonin dbe1a1
  #endif
Ivan Mahonin dbe1a1
Ivan Mahonin 51e530
  Atom ahps = XInternAtom(app->dpy, "_HILDON_PORTRAIT_MODE_SUPPORT", True);
Ivan Mahonin c456ae
  if (ahps) {
Ivan Mahonin c456ae
    LOGDBG("app: init: set portrait mode support for hildon");
Ivan Mahonin 51e530
    long v = 1;
Ivan Mahonin c456ae
    XChangeProperty(app->dpy, app->win, ahps, XA_CARDINAL, 32, PropModeReplace, (unsigned char*)&v, 1);
Ivan Mahonin c456ae
  }
Ivan Mahonin c456ae
Ivan Mahonin dbe1a1
  LOGDBG("app: init: set window minimum size");
Ivan Mahonin dbe1a1
  XSizeHints *wsh = XAllocSizeHints();
Ivan Mahonin dbe1a1
  if (!wsh) {
Ivan Mahonin dbe1a1
    LOGWRN("app: init: XAllocSizeHints error");
Ivan Mahonin dbe1a1
  } else {
Ivan Mahonin 4b2399
    #ifdef LOCK_SIZE
Ivan Mahonin 4b2399
    wsh->flags      = PMinSize | PMaxSize;
Ivan Mahonin 4b2399
    wsh->min_width  = wsh->max_width  = app->w;
Ivan Mahonin 4b2399
    wsh->min_height = wsh->max_height = app->h;
Ivan Mahonin 4b2399
    #else
Ivan Mahonin dbe1a1
    wsh->flags      = PMinSize;
Ivan Mahonin dbe1a1
    wsh->min_width  = MIN_WIDTH;
Ivan Mahonin dbe1a1
    wsh->min_height = MIN_HEIGHT;
Ivan Mahonin 4b2399
    #endif
Ivan Mahonin dbe1a1
    XSetSizeHints(app->dpy, app->win, wsh, XA_WM_NORMAL_HINTS);
Ivan Mahonin dbe1a1
    XFree(wsh);
Ivan Mahonin dbe1a1
  }
Ivan Mahonin dbe1a1
  
Ivan Mahonin dbe1a1
  // window must not grab the focus
Ivan Mahonin dbe1a1
  LOGDBG("app: init: set window input hint");
Ivan Mahonin dbe1a1
  XWMHints *wmh = XAllocWMHints();
Ivan Mahonin dbe1a1
  if (!wmh) {
Ivan Mahonin dbe1a1
    LOGWRN("app: init: XAllocWMHints error");
Ivan Mahonin dbe1a1
  } else {
Ivan Mahonin dbe1a1
    wmh->flags = InputHint;
Ivan Mahonin dbe1a1
    wmh->input = False;
Ivan Mahonin dbe1a1
    XSetWMHints(app->dpy, app->win, wmh);
Ivan Mahonin dbe1a1
    XFree(wmh);
Ivan Mahonin dbe1a1
  }
Ivan Mahonin dbe1a1
  
Ivan Mahonin 8864eb
  // subscribe to windows delete message from window manager
Ivan Mahonin 8864eb
  app->aWmDel = XInternAtom(app->dpy, "WM_DELETE_WINDOW", False);
Ivan Mahonin 8864eb
  XSetWMProtocols(app->dpy, app->win, &app->aWmDel, 1);
Ivan Mahonin 8864eb
  
Ivan Mahonin 8864eb
  // subscribe to keyboard state and layout changes
Ivan Mahonin 8864eb
  XkbSelectEvents(app->dpy, XkbUseCoreKbd, XkbMapNotifyMask | XkbStateNotifyMask, XkbMapNotifyMask | XkbStateNotifyMask);
Ivan Mahonin 8864eb
  
Ivan Mahonin e3897e
  #ifdef SCREEN_EVENTS
Ivan Mahonin e3897e
  // subscribe to xrandr events
Ivan Mahonin e3897e
  if (!XRRQueryExtension(app->dpy, &app->xrev, &app->xrerr)) {
Ivan Mahonin e3897e
    LOGWRN("app: init: XRRQueryExtension error");
Ivan Mahonin e3897e
  } else {
Ivan Mahonin e3897e
    app->xron = 1;
Ivan Mahonin e3897e
    XRRSelectInput(app->dpy, app->win, RRScreenChangeNotifyMask);
Ivan Mahonin e3897e
  }
Ivan Mahonin e3897e
  #endif
Ivan Mahonin e3897e
Ivan Mahonin 51e530
  //appMove(app, app->x, app->y, app->w, app->h, 1);
Ivan Mahonin 51e530
  //appUpdateStrut(app);
Ivan Mahonin 51e530
Ivan Mahonin dbe1a1
  // init submodules
Ivan Mahonin 7e0df9
  touchInit(&app->touch, app, touch_dev); // touch is optional
Ivan Mahonin dbe1a1
  if (graphInit(&app->graph, app)) {
Ivan Mahonin dbe1a1
    graphResize(&app->graph);
Ivan Mahonin dbe1a1
    if (inputInit(&app->input, app)) {
Ivan Mahonin dbe1a1
      if (keyboardInit(&app->keyboard, app)) {
Ivan Mahonin dbe1a1
        return 1;
Ivan Mahonin 452870
      }
Ivan Mahonin dbe1a1
      inputDeinit(&app->input);
Ivan Mahonin 452870
    }
Ivan Mahonin dbe1a1
    graphDeinit(&app->graph);
Ivan Mahonin 452870
  } else {
Ivan Mahonin dbe1a1
    XCloseDisplay(app->dpy);
Ivan Mahonin 452870
  }
Ivan Mahonin 452870
  
Ivan Mahonin 452870
  return 0;
Ivan Mahonin 452870
}
Ivan Mahonin 452870
Ivan Mahonin 452870
Ivan Mahonin 452870
void appDeinit(App *app) {
Ivan Mahonin 452870
  LOGDBG("app: deinit");
Ivan Mahonin 452870
  keyboardDeinit(&app->keyboard);
Ivan Mahonin 452870
  inputDeinit(&app->input);
Ivan Mahonin 452870
  graphDeinit(&app->graph);
Ivan Mahonin dbe1a1
  XCloseDisplay(app->dpy);
Ivan Mahonin 452870
}
Ivan Mahonin 452870
Ivan Mahonin 452870
Ivan Mahonin 452870
int appRun(App *app) {
Ivan Mahonin 452870
  LOGDBG("app: run");
Ivan Mahonin 452870
Ivan Mahonin 843e7a
  if (app->run)
Ivan Mahonin 843e7a
    return LOGERR("app: run: seems already run");
Ivan Mahonin 843e7a
  app->run = 1;
Ivan Mahonin 843e7a
  
Ivan Mahonin 452870
  LOGDBG("app: run: show window");
Ivan Mahonin dbe1a1
  XMapWindow(app->dpy, app->win);
Ivan Mahonin 325e70
  //appMove(app, app->x, app->y, app->w, app->h, 1);
Ivan Mahonin c456ae
  appUpdateStrut(app);
Ivan Mahonin 325e70
  //appMove(app, app->x, app->y, app->w, app->h, 1);
Ivan Mahonin 452870
Ivan Mahonin dbe1a1
  LOGDBG("app: run: get connection file descriptor");
Ivan Mahonin dbe1a1
  int fd = ConnectionNumber(app->dpy);
Ivan Mahonin 5b6cfc
  if (fd < 0)
Ivan Mahonin dbe1a1
    LOGERR("app: run: cannot get XLib file descruptor");
Ivan Mahonin 5b6cfc
    
Ivan Mahonin dbe1a1
  LOGDBG("app: run: event loop");
Ivan Mahonin 5b6cfc
  int buttonDown = 0;
Ivan Mahonin 5b6cfc
  unsigned int buttonDownMs = 0;
Ivan Mahonin 8603b4
Ivan Mahonin fd85b0
  struct pollfd fds[2] = {};
Ivan Mahonin 8603b4
  fds[0].fd = fd;
Ivan Mahonin fd85b0
  fds[1].fd = app->touch.fd;
Ivan Mahonin fd85b0
  fds[0].events = fds[1].events = POLLIN;
Ivan Mahonin fd85b0
  int fdcnt = app->touch.app ? 2 : 1;
Ivan Mahonin 7e0df9
Ivan Mahonin 96c481
  XFlush(app->dpy);
Ivan Mahonin 5b6cfc
  while(1) {
Ivan Mahonin dbe1a1
    int hasEvent = 0;
Ivan Mahonin dbe1a1
    XEvent event = {};
Ivan Mahonin fd85b0
    fds[0].revents = fds[1].revents = 0;
Ivan Mahonin 5b6cfc
    
Ivan Mahonin 96c481
    // wait for next event
Ivan Mahonin 96c481
    if (XPending(app->dpy)) {
Ivan Mahonin 96c481
      XNextEvent(app->dpy, &event);
Ivan Mahonin 96c481
      hasEvent = 1;
Ivan Mahonin 96c481
    } else
Ivan Mahonin 96c481
    if (buttonDown != 1) {
Ivan Mahonin 96c481
      // just wait for fds
Ivan Mahonin 3ec526
      poll(fds, fdcnt, -1);
Ivan Mahonin 5b6cfc
    } else {
Ivan Mahonin 96c481
      // manually call select for the X-socket to use timeout
Ivan Mahonin 8603b4
      int dt = monotonicMs() - buttonDownMs;
Ivan Mahonin 96c481
      if (dt < LONGPRESS_MS) {
Ivan Mahonin 96c481
        dt = LONGPRESS_MS - dt;
Ivan Mahonin 8603b4
        LOGDBG("app: run: use poll with timeout %d ms", dt);
Ivan Mahonin 96c481
        XFlush(app->dpy);
Ivan Mahonin 3ec526
        poll(fds, fdcnt, dt);
Ivan Mahonin 96c481
      }
Ivan Mahonin 96c481
    }
Ivan Mahonin 96c481
    
Ivan Mahonin 3ec526
    if (!hasEvent && fds[0].revents && XPending(app->dpy)) {
Ivan Mahonin dbe1a1
      XNextEvent(app->dpy, &event);
Ivan Mahonin dbe1a1
      hasEvent = 1;
Ivan Mahonin 452870
    }
Ivan Mahonin 5b6cfc
    
Ivan Mahonin dbe1a1
    if (hasEvent) {
Ivan Mahonin 7e0df9
      // handle X11 events
Ivan Mahonin dbe1a1
      switch(event.type) {
Ivan Mahonin dbe1a1
      case Expose: {
Ivan Mahonin dbe1a1
        LOGDBG( "app: expose: x=%d, y=%d, w=%d, h=%d",
Ivan Mahonin dbe1a1
                event.xexpose.x, event.xexpose.y, event.xexpose.width, event.xexpose.height );
Ivan Mahonin dbe1a1
        appInvalidateRect(app, event.xexpose.x, event.xexpose.y, event.xexpose.width, event.xexpose.height);
Ivan Mahonin 5b6cfc
        break;
Ivan Mahonin 452870
      }
Ivan Mahonin dbe1a1
      case ConfigureNotify: {
Ivan Mahonin dbe1a1
        if ( event.xconfigure.width
Ivan Mahonin dbe1a1
          && event.xconfigure.height
Ivan Mahonin dbe1a1
          && ( app->x != event.xconfigure.x
Ivan Mahonin dbe1a1
            || app->y != event.xconfigure.y
Ivan Mahonin dbe1a1
            || app->w != event.xconfigure.width
Ivan Mahonin dbe1a1
            || app->h != event.xconfigure.height ))
Ivan Mahonin 5b6cfc
        {
Ivan Mahonin dbe1a1
          LOGDBG( "app: moved: x=%d y=%d w=%d, h=%d",
Ivan Mahonin dbe1a1
                  event.xconfigure.x, event.xconfigure.y, event.xconfigure.width, event.xconfigure.height );
Ivan Mahonin dbe1a1
          int resized = app->w != event.xconfigure.width || app->h != event.xconfigure.height;
Ivan Mahonin dbe1a1
          app->x = event.xconfigure.x;
Ivan Mahonin dbe1a1
          app->y = event.xconfigure.y;
Ivan Mahonin dbe1a1
          app->w = event.xconfigure.width;
Ivan Mahonin dbe1a1
          app->h = event.xconfigure.height;
Ivan Mahonin 5b6cfc
          if (resized) graphResize(&app->graph);
Ivan Mahonin 51e530
          appUpdateStrut(app);
Ivan Mahonin 5b6cfc
        }
Ivan Mahonin 5b6cfc
        break;
Ivan Mahonin 452870
      }
Ivan Mahonin dbe1a1
      case ButtonPress: {
Ivan Mahonin dbe1a1
        if (event.xbutton.button == 1) {
Ivan Mahonin dbe1a1
          LOGDBG("app: mouse down: x=%d, y=%d", event.xbutton.x, event.xbutton.y);
Ivan Mahonin 5b6cfc
          if (!buttonDown) { buttonDown = 1; buttonDownMs = monotonicMs(); }
Ivan Mahonin dbe1a1
          keyboardMouseDown(&app->keyboard, event.xbutton.x, event.xbutton.y);
Ivan Mahonin 5b6cfc
        }
Ivan Mahonin 51e530
        appUpdateStrut(app);
Ivan Mahonin 5b6cfc
        break;
Ivan Mahonin 63daec
      }
Ivan Mahonin dbe1a1
      case ButtonRelease: {
Ivan Mahonin dbe1a1
        if (event.xbutton.button == 1) {
Ivan Mahonin 5b6cfc
          LOGDBG("app: mouse up");
Ivan Mahonin 5b6cfc
          buttonDown = 0;
Ivan Mahonin 5b6cfc
          keyboardMouseUp(&app->keyboard);
Ivan Mahonin 5b6cfc
        }
Ivan Mahonin 5b6cfc
        break;
Ivan Mahonin 5b6cfc
      }
Ivan Mahonin dbe1a1
      case MotionNotify: {
Ivan Mahonin dbe1a1
        if (event.xmotion.state & Button1Mask) {
Ivan Mahonin dbe1a1
          LOGDBG("app: mouse motion: x=%d, y=%d", event.xmotion.x, event.xmotion.y);
Ivan Mahonin dbe1a1
          keyboardMouseMotion(&app->keyboard, event.xmotion.x, event.xmotion.y);
Ivan Mahonin 5b6cfc
        }
Ivan Mahonin 452870
        break;
Ivan Mahonin 5b6cfc
      }
Ivan Mahonin 8864eb
      case MappingNotify: {
Ivan Mahonin 8864eb
        if ( event.xmapping.request == MappingModifier
Ivan Mahonin 8864eb
          || event.xmapping.request == MappingKeyboard )
Ivan Mahonin 8864eb
        {
Ivan Mahonin 8864eb
          LOGDBG("app: kayboard layout changed");
Ivan Mahonin 8864eb
          XRefreshKeyboardMapping(&event.xmapping);
Ivan Mahonin 8864eb
          inputUpdateLayout(
Ivan Mahonin 8864eb
            &app->input,
Ivan Mahonin 8864eb
            event.xmapping.first_keycode,
Ivan Mahonin 8864eb
            event.xmapping.first_keycode + event.xmapping.count );
Ivan Mahonin 8864eb
        }
Ivan Mahonin 8864eb
        break;
Ivan Mahonin 8864eb
      }
Ivan Mahonin 8864eb
      case ClientMessage: {
Ivan Mahonin 8864eb
        if (event.xclient.data.l[0] == app->aWmDel) {
Ivan Mahonin 8864eb
          LOGDBG("app: delete window event");
Ivan Mahonin 8864eb
          appStop(app, 0);
Ivan Mahonin 8864eb
        }
Ivan Mahonin 8864eb
        break;
Ivan Mahonin 8864eb
      }
Ivan Mahonin e3897e
      default:
Ivan Mahonin 13b128
        if (app->xron && event.type == app->xrev + RRScreenChangeNotify && XRRUpdateConfiguration(&event)) {
Ivan Mahonin e3897e
          LOGDBG("app: screen change event");
Ivan Mahonin 13b128
          Rotation r = 0;
Ivan Mahonin e3897e
          XRRScreenChangeNotifyEvent *ev = (XRRScreenChangeNotifyEvent*)&event;
Ivan Mahonin 13b128
          XRRRotations(app->dpy, app->screen, &r); // seems rotation in event is wrong
Ivan Mahonin 13b128
          appUpdateScreenSize(app, ev->width, ev->height, r);
Ivan Mahonin e3897e
        }
Ivan Mahonin e3897e
        break;
Ivan Mahonin 5b6cfc
      }
Ivan Mahonin 5b6cfc
    }
Ivan Mahonin 5b6cfc
    
Ivan Mahonin fd85b0
    if (app->touch.app && fds[1].revents) {
Ivan Mahonin 3ec526
      // handle direct touch events
Ivan Mahonin 3ec526
      int x, y, p;
Ivan Mahonin 3ec526
      while(touchGet(&app->touch, &x, &y, &p)) {
Ivan Mahonin 3ec526
        if (p && !buttonDown) {
Ivan Mahonin 3ec526
          LOGDBG("app: touch pressed: x=%d, y=%d", x, y);
Ivan Mahonin 3ec526
          buttonDown = 1; buttonDownMs = monotonicMs();
Ivan Mahonin 3ec526
          keyboardMouseDown(&app->keyboard, x, y);
Ivan Mahonin 3ec526
          hasEvent = 1;
Ivan Mahonin 3ec526
        } else
Ivan Mahonin 3ec526
        if (!p && buttonDown) {
Ivan Mahonin 3ec526
          LOGDBG("app: touch released");
Ivan Mahonin 3ec526
          buttonDown = 0;
Ivan Mahonin 3ec526
          keyboardMouseUp(&app->keyboard);
Ivan Mahonin 3ec526
          hasEvent = 1;
Ivan Mahonin 3ec526
        } else
Ivan Mahonin 3ec526
        if (p) {
Ivan Mahonin 3ec526
          LOGDBG("app: touch motion: x=%d, y=%d", x, y);
Ivan Mahonin 3ec526
          keyboardMouseMotion(&app->keyboard, x, y);
Ivan Mahonin 3ec526
          hasEvent = 1;
Ivan Mahonin 3ec526
        }
Ivan Mahonin 3ec526
      }
Ivan Mahonin 3ec526
    }
Ivan Mahonin 3ec526
    
Ivan Mahonin 5b6cfc
    if (buttonDown == 1 && monotonicMs() - buttonDownMs >= LONGPRESS_MS) {
Ivan Mahonin 5b6cfc
        LOGDBG("app: long press");
Ivan Mahonin 5b6cfc
        buttonDown = 2;
Ivan Mahonin 5b6cfc
        keyboardMouseLongDown(&app->keyboard);
Ivan Mahonin 3ec526
        hasEvent = 1;
Ivan Mahonin 452870
    }
Ivan Mahonin 452870
    
Ivan Mahonin 843e7a
    if (app->run != 1) break;
Ivan Mahonin 3ec526
    if (!hasEvent) continue;
Ivan Mahonin 843e7a
    
Ivan Mahonin 061fcf
    inputUpdateModifiers(&app->input);
Ivan Mahonin 061fcf
    keyboardResize(&app->keyboard);
Ivan Mahonin 061fcf
    keyboardUpdateModifiers(&app->keyboard);
Ivan Mahonin 452870
    if (app->irw > 0 && app->irh > 0) {
Ivan Mahonin 452870
      LOGDBG("app: draw: x=%d, y=%d, w=%d, h=%d", app->irx, app->iry, app->irw, app->irh);
Ivan Mahonin 452870
      graphDrawBackgound(&app->graph, app->irx, app->iry, app->irw, app->irh);
Ivan Mahonin 452870
      keyboardDraw(&app->keyboard, app->irx, app->iry, app->irw, app->irh);
Ivan Mahonin 452870
      app->irw = 0;
Ivan Mahonin 452870
    }
Ivan Mahonin 452870
  }
Ivan Mahonin 452870
  
Ivan Mahonin 843e7a
  int err = app->run == -2;
Ivan Mahonin 843e7a
  app->run = 0;
Ivan Mahonin 843e7a
  LOGDBG("app: run: done err=%d", err);
Ivan Mahonin 843e7a
  return !err;
Ivan Mahonin 843e7a
}
Ivan Mahonin 843e7a
Ivan Mahonin 843e7a
Ivan Mahonin 843e7a
void appStop(App *app, int err) {
Ivan Mahonin 843e7a
  LOGDBG("app: stop: err=%d", err);
Ivan Mahonin 843e7a
  if (!app->run) {
Ivan Mahonin 843e7a
    LOGERR("app: stop: seems not started");
Ivan Mahonin 843e7a
    return;
Ivan Mahonin 843e7a
  }
Ivan Mahonin 843e7a
  if (app->run == 1) app->run = -1;
Ivan Mahonin 843e7a
  if (err) app->run = -2;
Ivan Mahonin 843e7a
}
Ivan Mahonin 843e7a
Ivan Mahonin 843e7a
Ivan Mahonin c456ae
void appUpdateStrut(App *app) {
Ivan Mahonin c456ae
  #ifdef DOCK
Ivan Mahonin 51e530
  long v[12] = { 0, 0, 0, app->h, 0, app->sh, 0, app->sh, 0, app->sw, 0, app->sw };
Ivan Mahonin 51e530
Ivan Mahonin 51e530
  #ifdef TOP
Ivan Mahonin 51e530
  v[2] = app->h;
Ivan Mahonin 51e530
  #else
Ivan Mahonin 51e530
  v[3] = app->h;
Ivan Mahonin c456ae
  #endif
Ivan Mahonin c456ae
Ivan Mahonin c456ae
  if (app->dockt == v[2] && app->dockb == v[3])
Ivan Mahonin c456ae
    return;
Ivan Mahonin c456ae
  app->dockt = v[2];
Ivan Mahonin c456ae
  app->dockb = v[3];
Ivan Mahonin c456ae
Ivan Mahonin 51e530
  LOGDBG("app: update strut: %lu %lu %lu %lu", v[0], v[1], v[2], v[3]);
Ivan Mahonin 7e0df9
Ivan Mahonin c456ae
  Atom k = XInternAtom(app->dpy, "_NET_WM_STRUT", False);
Ivan Mahonin 51e530
  XChangeProperty(app->dpy, app->win, k, XA_CARDINAL, 32, PropModeReplace, (unsigned char*)v, 4);
Ivan Mahonin c456ae
Ivan Mahonin 51e530
  Atom kp = XInternAtom(app->dpy, "_NET_WM_STRUT_PARTIAL", False);
Ivan Mahonin 51e530
  XChangeProperty(app->dpy, app->win, kp, XA_CARDINAL, 32, PropModeReplace, (unsigned char*)v, 12);
Ivan Mahonin 51e530
Ivan Mahonin 51e530
  #ifdef TOP
Ivan Mahonin 51e530
  appMove(app, 0, 0, app->sw, app->h, 1);
Ivan Mahonin 51e530
  #else
Ivan Mahonin 51e530
  appMove(app, 0, app->sh - app->h, app->sw, app->h, 1);
Ivan Mahonin 51e530
  #endif
Ivan Mahonin 51e530
  #endif // DOCK
Ivan Mahonin c456ae
}
Ivan Mahonin c456ae
Ivan Mahonin c456ae
Ivan Mahonin 7e0df9
void appUpdateScreenSize(App *app, int sw, int sh, Rotation sr) {
Ivan Mahonin e3897e
  if (sw <= 0 || sh <= 0) return;
Ivan Mahonin c456ae
Ivan Mahonin 7e0df9
  if (sw == app->sw && sh == app->sh && sr == app->sr) return;
Ivan Mahonin 13b128
  LOGDBG("app: update screen size: w=%d, h=%d, r=%02x", sw, sh, sr);
Ivan Mahonin 7e0df9
Ivan Mahonin 7e0df9
  app->sr = sr;
Ivan Mahonin e3897e
  if (sw == app->sw && sh == app->sh) return;
Ivan Mahonin cbe4b8
Ivan Mahonin 460d1a
  #if defined(DOCK) || (defined(LOCK_SIZE) && defined(NOBORDER) && defined(NOTITLE))
Ivan Mahonin 56756f
  // static size case
Ivan Mahonin 56756f
  int w = sw * WIDTH_SCALE;
Ivan Mahonin 56756f
  int h = sh * HEIGHT_SCALE;
Ivan Mahonin 56756f
  #if defined(WIDTH_SCALE_P) && defined(HEIGHT_SCALE_P)
Ivan Mahonin 56756f
  if (sw < sh) {
Ivan Mahonin 56756f
    w = sw * WIDTH_SCALE_P;
Ivan Mahonin 56756f
    h = sh * HEIGHT_SCALE_P;
Ivan Mahonin 56756f
  }
Ivan Mahonin 56756f
  #endif
Ivan Mahonin 56756f
Ivan Mahonin 56756f
  int x0 = 0;
Ivan Mahonin 56756f
  #ifdef TOP
Ivan Mahonin 56756f
  int y0 = 0;
Ivan Mahonin 56756f
  #else
Ivan Mahonin 56756f
  int y0 = sh - h;
Ivan Mahonin 56756f
  #endif
Ivan Mahonin 56756f
Ivan Mahonin 56756f
  #else
Ivan Mahonin 56756f
  // dynamic size case
Ivan Mahonin cbe4b8
  double kx = sw/(double)app->sw;
Ivan Mahonin cbe4b8
  double ky = sh/(double)app->sh;
Ivan Mahonin cbe4b8
Ivan Mahonin cbe4b8
  #if defined(WIDTH_SCALE_P) && defined(HEIGHT_SCALE_P)
Ivan Mahonin cbe4b8
  // rescale for portrait/landscape switching
Ivan Mahonin cbe4b8
  if (app->sw < app->sh && sw >= sh) {
Ivan Mahonin cbe4b8
    // portrait -> landscape
Ivan Mahonin cbe4b8
    kx *= ((double)WIDTH_SCALE)/((double)WIDTH_SCALE_P);
Ivan Mahonin cbe4b8
    ky *= ((double)HEIGHT_SCALE)/((double)HEIGHT_SCALE_P);
Ivan Mahonin cbe4b8
  } else
Ivan Mahonin cbe4b8
  if (app->sw >= app->sh && sw < sh) {
Ivan Mahonin cbe4b8
    // landscape -> portrait
Ivan Mahonin cbe4b8
    kx *= ((double)WIDTH_SCALE_P)/((double)WIDTH_SCALE);
Ivan Mahonin cbe4b8
    ky *= ((double)HEIGHT_SCALE_P)/((double)HEIGHT_SCALE);
Ivan Mahonin cbe4b8
  }
Ivan Mahonin cbe4b8
  #endif
Ivan Mahonin cbe4b8
Ivan Mahonin cbe4b8
  // scale window corners
Ivan Mahonin cbe4b8
  int x0 = (int)round( app->x*kx );
Ivan Mahonin cbe4b8
  int y0 = (int)round( app->y*ky );
Ivan Mahonin cbe4b8
  int x1 = (int)round( (app->x + app->w)*kx );
Ivan Mahonin cbe4b8
  int y1 = (int)round( (app->y + app->h)*ky );
Ivan Mahonin 56756f
  int w = x1 - x0;
Ivan Mahonin 56756f
  int h = y1 - y0;
Ivan Mahonin 56756f
  #endif
Ivan Mahonin cbe4b8
Ivan Mahonin e3897e
  app->sw = sw;
Ivan Mahonin e3897e
  app->sh = sh;
Ivan Mahonin 51e530
  appMove(app, x0, y0, w, h, 0);
Ivan Mahonin e3897e
}
Ivan Mahonin e3897e
Ivan Mahonin e3897e
Ivan Mahonin 51e530
void appMove(App *app, int x, int y, int w, int h, int force) {
Ivan Mahonin db2d04
  if (w < MIN_WIDTH)  w = MIN_WIDTH;
Ivan Mahonin db2d04
  if (h < MIN_HEIGHT) h = MIN_HEIGHT;
Ivan Mahonin dbe1a1
  
Ivan Mahonin dbe1a1
  unsigned int mask = 0;
Ivan Mahonin dbe1a1
  XWindowChanges ch = {};
Ivan Mahonin 51e530
  if (force || app->x != x) ch.x = x, mask |= CWX;
Ivan Mahonin 51e530
  if (force || app->y != y) ch.y = y, mask |= CWY;
Ivan Mahonin 51e530
  if (force || app->w != w) ch.width  = w, mask |= CWWidth;
Ivan Mahonin 51e530
  if (force || app->h != h) ch.height = h, mask |= CWHeight;
Ivan Mahonin dbe1a1
  if (!mask) return;
Ivan Mahonin dbe1a1
  
Ivan Mahonin 51e530
  LOGDBG("app: move: x=%d, y=%d, w=%d, h=%d", x, y, w, h);
Ivan Mahonin dbe1a1
  XConfigureWindow(app->dpy, app->win, mask, &ch);
Ivan Mahonin db2d04
  XFlush(app->dpy);
Ivan Mahonin 452870
}
Ivan Mahonin 452870
Ivan Mahonin 452870
Ivan Mahonin 452870
void appInvalidateRect(App *app, int x, int y, int w, int h) {
Ivan Mahonin 452870
  LOGDBG("app: invalidate rect: x=%d, y=%d, w=%d, h=%d", x, y, w, h);
Ivan Mahonin 452870
  rectIntersect(&x, &y, &w, &h, 0, 0, app->w, app->h);
Ivan Mahonin 452870
  rectMerge(&app->irx, &app->iry, &app->irw, &app->irh, x, y, w, h);
Ivan Mahonin d3e9d7
  LOGDBG("app: invalidate rect: summary x=%d, y=%d, w=%d, h=%d", app->irx, app->iry, app->irw, app->irh);
Ivan Mahonin 452870
}
Ivan Mahonin 452870