Logo Search packages:      
Sourcecode: w3mmee version File versions  Download package

x11_w3mimg.c

#include "w3mimg/w3mimg.h"

#if defined(USE_IMLIB)
#include <Imlib.h>
#elif defined(USE_GDKPIXBUF)
#include <gdk-pixbuf/gdk-pixbuf-xlib.h>
#else
#error neither Imlib nor GdkPixbuf support
#endif

struct x11_w3mimg_desc {
  Display *display;
  Window window, parent;
  unsigned long background_pixel;
  Region screenRegion, clipRegion, exposureRegion, imageRegion, curRegion;
  GC imageGC;
#if defined(USE_IMLIB)
  ImlibData *id;
#elif defined(USE_GDKPIXBUF)
  int init_flag;
#endif
  XPoint screenRectangle[5];
  int prev_xexpose_count, nsyncs, nevents;
  int ignore_xexpose;
};

#if defined(USE_GDKPIXBUF)
struct x11_w3mimg_pixmaps {
  int total;
  int no;
  int wait;
  Pixmap *pixmap;
};
#endif

static void
x11_img_init(w3mimg_desc *desc)
{
  struct x11_w3mimg_desc *xdesc;

#if defined(USE_IMLIB)
  if ((xdesc = desc->priv) &&
      !xdesc->id && !(xdesc->id = Imlib_init(xdesc->display))) {
    XCloseDisplay(xdesc->display);
    memset(xdesc, 0, sizeof(*xdesc));
    desc->priv = NULL;
  }
#elif defined(USE_GDKPIXBUF)
  if ((xdesc = desc->priv) && !xdesc->init_flag) {
    gdk_pixbuf_xlib_init(xdesc->display, 0);
    xdesc->init_flag = TRUE;
  }
#endif
}

static void
x11_img_free(w3mimg_desc *desc, W3MImage *image)
{
  struct x11_w3mimg_desc *xdesc;
#ifndef USE_GDKPIXBUF
  Pixmap map;

  if ((xdesc = desc->priv) && (map = (Pixmap)image->pixmap)) {
    XFreePixmap(xdesc->display, map);
    image->pixmap = NULL;
  }
#else
  struct x11_w3mimg_pixmaps *map;

  if ((xdesc = desc->priv) && (map = image->pixmap)) {
    if (map->pixmap) {
      int i;

      for (i = map->total ; i > 0 ;)
      if (map->pixmap[--i])
        XFreePixmap(xdesc->display, map->pixmap[i]);

      free(map->pixmap);
    }

    free(map);
    image->pixmap = NULL;
  }
#endif
}

#if defined(USE_GDKPIXBUF)
static struct x11_w3mimg_pixmaps *
x11_img_new_pixmap(struct x11_w3mimg_desc *xi, int w, int h, int n)
{
  struct x11_w3mimg_pixmaps *img = NULL;
  int i;

  if (!(img = malloc(sizeof(*img))))
    goto error;

  if (!(img->pixmap = calloc(n, sizeof(*(img->pixmap)))))
    goto error;

  XSetClipMask(xi->display, xi->imageGC, None);
  XSetForeground(xi->display, xi->imageGC, xi->background_pixel);

  for (i = n ; i > 0 ;) {
    if (!(img->pixmap[--i] = XCreatePixmap(xi->display, xi->parent, w, h,
                                 DefaultDepth(xi->display, 0))))
      goto error;

    XFillRectangle(xi->display, img->pixmap[i], xi->imageGC, 0, 0, w, h);
  }

  img->no = 0;
  img->total = n;
  img->wait = 0;
  return img;
error:
  if (img) {
    if (img->pixmap) {
      for (i = n ; i > 0 ;)
      if (img->pixmap[--i])
        XFreePixmap(xi->display, img->pixmap[i]);

      free(img->pixmap);
    }

    free(img);
  }

  return NULL;
}

static GdkPixbuf *
x11_img_resize_pixbuf(GdkPixbuf *pixbuf, int width, int height)
{
  int w, h;

  if (!pixbuf || width < 1 || height < 1)
    return pixbuf;

  w = gdk_pixbuf_get_width(pixbuf);
  h = gdk_pixbuf_get_height(pixbuf);

  if (w == width && h == height)
    return pixbuf;

  return gdk_pixbuf_scale_simple(pixbuf, width, height, GDK_INTERP_BILINEAR);
}
#endif

static void
x11_img_load(w3mimg_desc *desc, W3MImage *image, char *p, int w, int h)
{
  struct x11_w3mimg_desc *xdesc;

  if ((xdesc = desc->priv)) {
#if defined(USE_IMLIB)
    ImlibImage *im;

    if ((im = Imlib_load_image(xdesc->id, p))) {
      if (w <= 0)
      w = im->rgb_width;

      if (h <= 0)
      h = im->rgb_height;

      if (!(image->pixmap = (void *)XCreatePixmap(xdesc->display, xdesc->parent, w, h,
                                      DefaultDepth(xdesc->display, 0)))) {
      Imlib_kill_image(xdesc->id, im);
      return;
      }

      if (!xdesc->imageGC &&
        !(xdesc->imageGC = XCreateGC(xdesc->display, xdesc->parent, 0, NULL))) {
      Imlib_kill_image(xdesc->id, im);
      return;
      }

      XSetClipMask(xdesc->display, xdesc->imageGC, None);
      XSetForeground(xdesc->display, xdesc->imageGC, xdesc->background_pixel);
      XFillRectangle(xdesc->display, (Pixmap)image->pixmap, xdesc->imageGC, 0, 0, w, h);
      Imlib_paste_image(xdesc->id, im, (Pixmap)image->pixmap, 0, 0, w, h);
      Imlib_kill_image(xdesc->id, im);
      image->width = w;
      image->height = h;
    }
#elif defined(USE_GDKPIXBUF)
    GdkPixbufAnimation *animation;

    if ((animation = gdk_pixbuf_animation_new_from_file(p))) {
      GList *frames;
      int i, iw, ih, n;
      double ratio_w, ratio_h;
      struct x11_w3mimg_pixmaps *ximg;
      GdkPixbufFrameAction action = GDK_PIXBUF_FRAME_REVERT;

      frames = gdk_pixbuf_animation_get_frames(animation);
      n = gdk_pixbuf_animation_get_num_frames(animation);
      iw = gdk_pixbuf_animation_get_width(animation);
      ih = gdk_pixbuf_animation_get_height(animation);

      if (w < 1 || h < 1) {
      w = iw;
      h = ih;
      ratio_w = ratio_h = 1;
      }
      else {
      ratio_w = 1.0 * w / iw;
      ratio_h = 1.0 * h / ih;
      }

      if (!xdesc->imageGC &&
        !(xdesc->imageGC = XCreateGC(xdesc->display, xdesc->parent, 0, NULL))) {
      gdk_pixbuf_animation_unref(animation);
      return;
      }

      if (!(ximg = x11_img_new_pixmap(xdesc, w, h, n))) {
      gdk_pixbuf_animation_unref(animation);
      return;
      }

      for (i = 0 ; i < n ; ++i) {
      GdkPixbufFrame *frame;
      GdkPixbuf *org_pixbuf, *pixbuf;
      int width, height, ofstx, ofsty;

      frame = (GdkPixbufFrame *)g_list_nth_data(frames, i);
      org_pixbuf = gdk_pixbuf_frame_get_pixbuf(frame);
      ofstx = gdk_pixbuf_frame_get_x_offset(frame);
      ofsty = gdk_pixbuf_frame_get_y_offset(frame);
      width = gdk_pixbuf_get_width(org_pixbuf);
      height = gdk_pixbuf_get_height(org_pixbuf);

      if (!ofstx && !ofsty && width == w && height == h)
        pixbuf = x11_img_resize_pixbuf(org_pixbuf, w, h);
      else {
        pixbuf = x11_img_resize_pixbuf(org_pixbuf, width * ratio_w, height * ratio_h);
        ofstx *= ratio_w;
        ofsty *= ratio_h;
      }

      width = gdk_pixbuf_get_width(pixbuf);
      height = gdk_pixbuf_get_height(pixbuf);

      if (i > 0)
        switch (action) {
        case GDK_PIXBUF_FRAME_RETAIN:
          XCopyArea(xdesc->display, ximg->pixmap[i - 1], ximg->pixmap[i],
                  xdesc->imageGC, 0, 0, w, h, 0, 0);
          break;
        case GDK_PIXBUF_FRAME_DISPOSE:
          break;
        case GDK_PIXBUF_FRAME_REVERT:
          XCopyArea(xdesc->display, ximg->pixmap[0], ximg->pixmap[i],
                  xdesc->imageGC, 0, 0, w, h, 0, 0);
          break;
        default:
          XCopyArea(xdesc->display, ximg->pixmap[0], ximg->pixmap[i],
                  xdesc->imageGC, 0, 0, w, h, 0, 0);
          break;
        }

      gdk_pixbuf_xlib_render_to_drawable_alpha(pixbuf,
                                     (Drawable)ximg->pixmap[i], 0,
                                     0, ofstx, ofsty, width,
                                     height,
                                     GDK_PIXBUF_ALPHA_BILEVEL, 1,
                                     XLIB_RGB_DITHER_NORMAL, 0, 0);
      action = gdk_pixbuf_frame_get_action(frame);

      if (org_pixbuf != pixbuf)
        gdk_pixbuf_finalize(pixbuf);
      }

      gdk_pixbuf_animation_unref(animation);
      image->pixmap = ximg;
      image->width = w;
      image->height = h;
    }
#endif
  }
}

static void
x11_img_clip(w3mimg_desc *desc, char *p)
{
  struct x11_w3mimg_desc *xdesc;

  if ((xdesc = desc->priv)) {
    char *ep;
    int i;
    short val[4];
    XRectangle rect;

    if (xdesc->exposureRegion && !XEmptyRegion(xdesc->exposureRegion))
      XSubtractRegion(xdesc->exposureRegion, xdesc->exposureRegion, xdesc->exposureRegion);

    if (xdesc->clipRegion)
      XSubtractRegion(xdesc->clipRegion, xdesc->clipRegion, xdesc->clipRegion);
    else if (!(xdesc->clipRegion = XCreateRegion()))
      w3mimg_exit(1, stderr, "clipRegion = XCreateRegion(): %s\n", strerror(errno));

    for (i = 0 ; *p ;) {
      val[i++] = strtol(p, &ep, 10);

      if (i == sizeof(val) / sizeof(val[0])) {
      rect.x = val[0] + desc->offset_x;
      rect.y = val[1] + desc->offset_y;
      rect.width = val[2];
      rect.height = val[3];
      XUnionRectWithRegion(&rect, xdesc->clipRegion, xdesc->clipRegion);
      i = 0;
      }

      p = *ep ? ep + 1 : ep;
    }
  }
}

static void
x11_clear(w3mimg_desc *desc)
{
  struct x11_w3mimg_desc *xdesc;

  if ((xdesc = desc->priv)) {
    if (xdesc->clipRegion) {
      XDestroyRegion(xdesc->clipRegion);
      xdesc->clipRegion = NULL;
    }

    if (xdesc->exposureRegion) {
      XDestroyRegion(xdesc->exposureRegion);
      xdesc->exposureRegion = NULL;
    }

    if (xdesc->screenRegion) {
      XDestroyRegion(xdesc->screenRegion);
      xdesc->screenRegion = NULL;
    }

    if (xdesc->imageRegion) {
      XDestroyRegion(xdesc->imageRegion);
      xdesc->imageRegion = NULL;
    }

    xdesc->curRegion = NULL;

    if (xdesc->imageGC) {
      XFreeGC(xdesc->display, xdesc->imageGC);
      xdesc->imageGC = NULL;
    }
  }
}

static int
x11_img_flush_start(w3mimg_desc *desc)
{
  struct x11_w3mimg_desc *xdesc;

  if (!(xdesc = desc->priv) ||
      XEmptyRegion(xdesc->curRegion) ||
      (!xdesc->imageGC &&
       !(xdesc->imageGC = XCreateGC(xdesc->display, xdesc->parent, 0, NULL))))
    return 0;

  if ((desc->clipped = xdesc->clipRegion && !XEmptyRegion(xdesc->clipRegion))) {
    if (xdesc->imageRegion) {
      if (!XEmptyRegion(xdesc->imageRegion))
      XSubtractRegion(xdesc->imageRegion, xdesc->imageRegion, xdesc->imageRegion);
    }
    else if (!(xdesc->imageRegion = XCreateRegion()))
      return 0;

    XSubtractRegion(xdesc->curRegion, xdesc->clipRegion, xdesc->imageRegion);
    XSetRegion(xdesc->display, xdesc->imageGC, xdesc->imageRegion);
  }
  else if (xdesc->curRegion == xdesc->screenRegion)
    XSetClipMask(xdesc->display, xdesc->imageGC, None);
  else
    XSetRegion(xdesc->display, xdesc->imageGC, xdesc->curRegion);

  desc->alldirty = xdesc->curRegion == xdesc->exposureRegion;
  return 1;
}

static void
x11_img_show(w3mimg_desc *desc, W3MImage *image,
           int sx, int sy, int sw, int sh, int x, int y)
{
  struct x11_w3mimg_desc *xdesc;

  if ((xdesc = desc->priv)) {
#if defined(USE_IMLIB)
    XCopyArea(xdesc->display, (Pixmap)image->pixmap, xdesc->window, xdesc->imageGC,
            sx, sy, sw, sh, x, y);
#elif defined(USE_GDKPIXBUF)
#define WAIT_CNT 4
    struct x11_w3mimg_pixmaps *ximg;
    int i;

    ximg = image->pixmap;
    i = ximg->no;
    XCopyArea(xdesc->display, ximg->pixmap[i], xdesc->window, xdesc->imageGC,
            sx, sy,
            (sw ? sw : image->width),
            (sh ? sh : image->height), x + desc->offset_x, y + desc->offset_y);

    if (ximg->total > 1) {
      if (ximg->wait > WAIT_CNT) {
      ximg->wait = 0;

      if (i < ximg->total - 1)
        ximg->no = i + 1;
      else
        ximg->no = 0;
      }

      ximg->wait += 1;
    }
#endif

    desc->flush_dirty = 1;
  }
}

static int
x11_img_clipped(w3mimg_desc *desc, W3MImage *image, int x, int y, int w, int h)
{
  struct x11_w3mimg_desc *xdesc;

  if ((xdesc = desc->priv))
    switch (XRectInRegion(xdesc->clipRegion, x, y, w, h)) {
    case RectangleIn:
    case RectanglePart:
      return 1;
    default:
      break;
    }

  return 0;
}

static void
x11_serv_write(w3mimg_desc *desc)
{
  struct x11_w3mimg_desc *xdesc;

  if ((xdesc = desc->priv)) {
    XFlush(xdesc->display);
    desc->flush_dirty = 0;
  }
}

static void
x11_serv_read(w3mimg_desc *desc)
{
  struct x11_w3mimg_desc *xdesc;

  if ((xdesc = desc->priv) && XEventsQueued(xdesc->display, QueuedAfterReading)) {
    XEvent event;

    do {
      XNextEvent(xdesc->display, &event);

      if (event.type == Expose && event.xexpose.window == xdesc->window) {
      XRectangle rect;

      if (!xdesc->prev_xexpose_count && xdesc->exposureRegion && !XEmptyRegion(xdesc->exposureRegion))
        XSubtractRegion(xdesc->exposureRegion, xdesc->exposureRegion, xdesc->exposureRegion);

      if (!xdesc->exposureRegion &&
          !(xdesc->exposureRegion = XCreateRegion()))
        w3mimg_exit(1, stderr, "xdesc->exposureRegion = XCreateRegion(): %s\n", strerror(errno));

      rect.x = event.xexpose.x;
      rect.y = event.xexpose.y;
      rect.width = event.xexpose.width;
      rect.height = event.xexpose.height;
      XUnionRectWithRegion(&rect, xdesc->exposureRegion, xdesc->exposureRegion);

      if (!(xdesc->prev_xexpose_count = event.xexpose.count))
        xdesc->nevents += desc->flush_delay;
      }
    } while (XEventsQueued(xdesc->display, QueuedAlready));
  }
}

static void
x11_serv_sync(w3mimg_desc *desc)
{
  struct x11_w3mimg_desc *xdesc;

  if ((xdesc = desc->priv))
    xdesc->nsyncs += desc->flush_delay;
}

static void
x11_serv_idle(w3mimg_desc *desc)
{
  struct x11_w3mimg_desc *xdesc;

  if ((xdesc = desc->priv)) {
    if (xdesc->nsyncs) {
      if (!--(xdesc->nsyncs)) {
      if (xdesc->exposureRegion && !XEmptyRegion(xdesc->exposureRegion))
        XSubtractRegion(xdesc->exposureRegion, xdesc->exposureRegion, xdesc->exposureRegion);

      if (!xdesc->screenRegion &&
          !(xdesc->screenRegion = XPolygonRegion(xdesc->screenRectangle,
                                       sizeof(xdesc->screenRectangle) / sizeof(xdesc->screenRectangle[0]),
                                       WindingRule)))
        w3mimg_exit(1, stderr, "xdesc->screenRegion = XPolygonRegion(): %s\n", strerror(errno));

      xdesc->curRegion = xdesc->screenRegion;
      w3mimg_flush(desc);
      xdesc->nevents = 0;
      xdesc->prev_xexpose_count = -1;
      }
    }
    else if (xdesc->nevents) {
      if (!--(xdesc->nevents) && !xdesc->ignore_xexpose && xdesc->exposureRegion && !XEmptyRegion(xdesc->exposureRegion)) {
      xdesc->curRegion = xdesc->exposureRegion;
      w3mimg_flush(desc);
      }
    }
  }
}

static void
x11_serv_close(w3mimg_desc *desc)
{
  struct x11_w3mimg_desc *xdesc;

  if ((xdesc = desc->priv)) {
    XCloseDisplay(xdesc->display);
    desc->priv = NULL;
  }
}

/*
  xterm/kterm/hanterm/cxterm
    top window (WINDOWID)
      +- text window
           +- scrollbar
  rxvt/aterm/Eterm/wterm
    top window (WINDOWID)
      +- text window
      +- scrollbar
      +- menubar (etc.)
  gnome-terminal
    top window
      +- text window (WINDOWID)
      +- scrollbar
      +- menubar
  mlterm (-s)
    top window
      +- text window (WINDOWID)
      +- scrollbar
  mlterm
    top window = text window (WINDOWID)

  powershell
    top window
      +- window
      |    +- text window
      |    +- scrollbar
      +- menubar (etc.)
  dtterm
    top window
      +- window
           +- window
           |    +- window
           |         +- text window
           |         +- scrollbar
           +- menubar
  hpterm
    top window
      +- window
           +- text window
           +- scrollbar
           +- (etc.)
*/

w3mimg_desc *
w3mimg_x11open(w3mimg_desc *desc, w3mimg_opts *opts)
{
  static struct x11_w3mimg_desc xdesc;
  char *id;
  int revert, nchildren, i;
  XWindowAttributes attr;
  Window root, *children;
  XColor screen_def, exact_def;

  memset(&xdesc, 0, sizeof(xdesc));
  xdesc.prev_xexpose_count = -1;
  desc->priv = NULL;
  desc->serv_fd = -1;

  if (!(xdesc.display = XOpenDisplay(NULL)))
    return NULL;

  if ((id = getenv("WINDOWID")) && *id)
    xdesc.window = (Window)strtol(id, NULL, 0);
  else
    XGetInputFocus(xdesc.display, &xdesc.window, &revert);

  if (!xdesc.window) {
    XCloseDisplay(xdesc.display);
    return NULL;
  }

  XGetWindowAttributes(xdesc.display, xdesc.window, &attr);
  desc->width = attr.width;
  desc->height = attr.height;
  desc->depth = attr.depth;

  while (1) {
    Window p_window;

    XQueryTree(xdesc.display, xdesc.window, &root, &xdesc.parent, &children, &nchildren);

    if (opts->defined_debug)
      fprintf(stderr,
            "window=%lx root=%lx parent=%lx nchildren=%d width=%d height=%d\n",
            (unsigned long)xdesc.window, (unsigned long)root, (unsigned long)xdesc.parent,
            nchildren, desc->width, desc->height);

    p_window = xdesc.window;

    for (i = 0 ; i < nchildren ; ++i) {
      XGetWindowAttributes(xdesc.display, children[i], &attr);

      if (opts->defined_debug)
      fprintf(stderr,
            "children[%d]=%lx x=%d y=%d width=%d height=%d\n", i,
            (unsigned long)children[i], attr.x, attr.y, attr.width, attr.height);

      if (attr.width > desc->width * 0.7 && attr.height > desc->height * 0.7) {
      /* maybe text window */
      desc->width = attr.width;
      desc->height = attr.height;
      desc->depth = attr.depth;
      xdesc.window = children[i];
      }
    }

    if (p_window == xdesc.window)
      break;
  }

  if (!opts->defined_x) {
    desc->offset_x = desc->offset_y = 2; /* ??? */

    for (i = 0 ; i < nchildren ; ++i) {
      XGetWindowAttributes(xdesc.display, children[i], &attr);

      if (attr.x <= 0 && attr.width < 30 && attr.height > desc->height * 0.7) {
      if (opts->defined_debug)
        fprintf(stderr,
              "children[%d]=%lx x=%d y=%d width=%d height=%d\n",
              i, (unsigned long)children[i], attr.x, attr.y, attr.width, attr.height);

      /* scrollbar of xterm/kterm ? */
      desc->offset_x += attr.x + attr.width + attr.border_width * 2;
      break;
      }
    }
  }

  xdesc.screenRectangle[1].x = xdesc.screenRectangle[2].x = desc->width;
  xdesc.screenRectangle[2].y = xdesc.screenRectangle[3].y = desc->height;

  if (opts->defined_bg && XAllocNamedColor(xdesc.display, DefaultColormap(xdesc.display, 0),
                                 opts->background, &screen_def, &exact_def))
    xdesc.background_pixel = screen_def.pixel;
  else {
    XImage *winimg;

    if ((winimg = XGetImage(xdesc.display, xdesc.window,
                      desc->offset_x > 0 ? desc->offset_x - 1 : 0,
                      desc->offset_y > 0 ? desc->offset_y - 1 : 0,
                      1, 1, AllPlanes, XYPixmap))) {
      xdesc.background_pixel = XGetPixel(winimg, 0, 0);
      XDestroyImage(winimg);
    }
    else
      xdesc.background_pixel = WhitePixel(xdesc.display, 0);
  }

  if (!(xdesc.ignore_xexpose = opts->defined_ignore_exposure))
    XSelectInput(xdesc.display, xdesc.window, ExposureMask);

  desc->serv_fd = ConnectionNumber(xdesc.display);
  desc->init_func = x11_img_init;
  desc->free_func = x11_img_free;
  desc->load_func = x11_img_load;
  desc->clip_func = x11_img_clip;
  desc->clip_test = x11_img_clipped;
  desc->clear_func = x11_clear;
  desc->flush_start_hook = x11_img_flush_start;
  desc->show_func = x11_img_show;
  desc->serv_write = x11_serv_write;
  desc->serv_read = x11_serv_read;
  desc->serv_sync = x11_serv_sync;
  desc->serv_idle = x11_serv_idle;
  desc->serv_close = x11_serv_close;
  desc->priv = &xdesc;
  return desc;
}

Generated by  Doxygen 1.6.0   Back to index