Logo Search packages:      
Sourcecode: kdenetwork version File versions

desktop.c

/*
 *  Copyright (C) 1999 AT&T Laboratories Cambridge.  All Rights Reserved.
 *  Copyright (C) 2002 Tim Jansen. All Rights Reserved.
 *  Copyright (C) 1999-2001 Anders Lindström
 * 
 *  
 *
 *  This 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 2 of the License, or
 *  (at your option) any later version.
 *
 *  This software 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 software; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
 *  USA.
 *
 * tim@tjansen.de: - removed stuff for krdc 
 *                 - merged with shm.c and misc.c
 *                 - added FillRectangle and Sync methods to draw only on 
 *                   the image
 *                 - added Zoom functionality, based on rotation funcs from
 *                   SGE by Anders Lindström)
 *                 - added support for softcursor encoding
 *
 */

/*
 * desktop.c - functions to deal with "desktop" window.
 */

#include <X11/Xlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <X11/extensions/XShm.h>
#include <math.h>
#include <limits.h>
#include "vncviewer.h"

static XShmSegmentInfo shminfo;
static Bool caughtShmError = False;
static Bool needShmCleanup = False;

static XShmSegmentInfo zoomshminfo;
static Bool caughtZoomShmError = False;
static Bool needZoomShmCleanup = False;

static Bool gcInited = False;
GC gc;
GC srcGC, dstGC; /* used for debugging copyrect */
Dimension dpyWidth, dpyHeight;

static XImage *image = NULL;
Bool useShm = True;

static Bool zoomActive = False;
static int zoomWidth, zoomHeight;
static XImage *zoomImage = NULL;
static Bool useZoomShm = True;

/* for softcursor */
static char *savedArea = NULL;

typedef enum {
  SOFTCURSOR_UNDER,
  SOFTCURSOR_PART_UNDER,
  SOFTCURSOR_UNAFFECTED
} SoftCursorState;

typedef int Sint32;
typedef short Sint16;
typedef char Sint8;
typedef unsigned int Uint32;
typedef unsigned short Uint16;
typedef unsigned char Uint8;

typedef struct {
  int w, h;
  unsigned int pitch;
  void *pixels;
  int BytesPerPixel;
} Surface;

typedef struct {
        Sint16 x, y;
        Uint16 w, h;
} Rect;

static void bgr233cpy(CARD8 *dst, CARD8 *src, int len);
static void CopyDataToScreenRaw(char *buf, int x, int y, int width, int height);
static void CopyBGR233ToScreen(CARD8 *buf, int x, int y, int width,int height);
static void FillRectangleBGR233(CARD8 buf, int x, int y, int width,int height);
static int CheckRectangle(int x, int y, int width, int height);
static SoftCursorState getSoftCursorState(int x, int y, int width, int height);
static void discardCursorSavedArea(void);
static void saveCursorSavedArea(void);

static void ZoomInit(void);
static void transformZoomSrc(int six, int siy, int siw, int sih,
                       int *dix, int *diy, int *diw, int *dih,
                       int srcW, int dstW, int srcH, int dstH);
static void transformZoomDst(int *six, int *siy, int *siw, int *sih,
                       int dix, int diy, int diw, int dih,
                       int srcW, int dstW, int srcH, int dstH);
static void ZoomSurfaceSrcCoords(int x, int y, int w, int h, 
                         int *dix, int *diy, int *diw, int *dih,
                         Surface * src, Surface * dst);
static void ZoomSurfaceCoords32(int sx, int sy, int sw, int sh,
                        int dx, int dy, Surface * src, Surface * dst);
static void sge_transform(Surface *src, Surface *dst, float xscale, float yscale,
                    Uint16 qx, Uint16 qy);


void
DesktopInit(Window win)
{
  XGCValues gcv;

  image = CreateShmImage();

  if (!image) {
    useShm = False;
    image = XCreateImage(dpy, vis, visdepth, ZPixmap, 0, NULL,
                   si.framebufferWidth, si.framebufferHeight,
                   BitmapPad(dpy), 0);

    image->data = calloc(image->bytes_per_line * image->height, 1);
    if (!image->data) {
      fprintf(stderr,"malloc failed\n");
      exit(1);
    }
  }

  gc = XCreateGC(dpy,win,0,NULL);

  gcv.function = GXxor;
  gcv.foreground = 0x0f0f0f0f;
  srcGC = XCreateGC(dpy,win,GCFunction|GCForeground,&gcv);
  gcv.foreground = 0xf0f0f0f0;
  dstGC = XCreateGC(dpy,win,GCFunction|GCForeground,&gcv);
  gcInited = True;
}

/*
 * DrawScreenRegionX11Thread
 * Never call from any other desktop.c function, only for X11 thread
 */

void
DrawScreenRegionX11Thread(Window win, int x, int y, int width, int height) {
  zoomActive = False;
  zoomWidth = 0;
  zoomHeight = 0;

  if (!image)
    return;

  if (useShm) 
    XShmPutImage(dpy, win, gc, image, x, y, x, y, width, height, False);
  else
    XPutImage(dpy, win, gc, image, x, y, x, y, width, height);
}

/*
 * CheckRectangle
 */

static int CheckRectangle(int x, int y, int width, int height) {
  if ((x < 0) || (y < 0))
    return 0;

  if (((x+width) > si.framebufferWidth) || ((y+height) > si.framebufferHeight))
    return 0;

  return 1;
}

static 
void bgr233cpy(CARD8 *dst, CARD8 *src, int len) {
  int i;
  CARD16 *d16;
  CARD32 *d32;

  switch (visbpp) {
  case 8:
    for (i = 0; i < len; i++) 
      *(dst++) = (CARD8) BGR233ToPixel[*(src++)];
    break;
  case 16:
    d16 = (CARD16*) dst;
    for (i = 0; i < len; i++) 
      *(d16++) = (CARD16) BGR233ToPixel[*(src++)];
    break;
  case 32:
    d32 = (CARD32*) dst;
    for (i = 0; i < len; i++) 
      *(d32++) = (CARD32) BGR233ToPixel[*(src++)];
    break;
  default:
    fprintf(stderr, "Unsupported softcursor depth %d\n", visbpp);
  }
}


/*
 * CopyDataToScreen.
 */

void
CopyDataToScreen(char *buf, int x, int y, int width, int height)
{
  SoftCursorState s;

  if (!CheckRectangle(x, y, width, height))
    return;

  LockFramebuffer();
  s = getSoftCursorState(x, y, width, height);
  if (s == SOFTCURSOR_PART_UNDER)
    undrawCursor();

  if (!appData.useBGR233)
    CopyDataToScreenRaw(buf, x, y, width, height);
  else
    CopyBGR233ToScreen((CARD8 *)buf, x, y, width, height);

  if (s != SOFTCURSOR_UNAFFECTED)
    drawCursor();

  UnlockFramebuffer();
  SyncScreenRegion(x, y, width, height);
}

/*
 * CopyDataToScreenRaw.
 */

static void
CopyDataToScreenRaw(char *buf, int x, int y, int width, int height)
{
  int h;
  int widthInBytes = width * visbpp / 8;
  int scrWidthInBytes = image->bytes_per_line;
  char *scr = (image->data + y * scrWidthInBytes
             + x * visbpp / 8);
  
  for (h = 0; h < height; h++) {
    memcpy(scr, buf, widthInBytes);
    buf += widthInBytes;
    scr += scrWidthInBytes;
  }
}

/*
 * CopyBGR233ToScreen.
 */

static void
CopyBGR233ToScreen(CARD8 *buf, int x, int y, int width, int height)
{
  int p, q;
  int xoff = 7 - (x & 7);
  int xcur;
  int fbwb = si.framebufferWidth / 8;
  CARD8 *scr1 = ((CARD8 *)image->data) + y * fbwb + x / 8;
  CARD8 *scrt;
  CARD8 *scr8 = ((CARD8 *)image->data) + y * si.framebufferWidth + x;
  CARD16 *scr16 = ((CARD16 *)image->data) + y * si.framebufferWidth + x;
  CARD32 *scr32 = ((CARD32 *)image->data) + y * si.framebufferWidth + x;

  switch (visbpp) {

    /* thanks to Chris Hooper for single bpp support */

  case 1:
    for (q = 0; q < height; q++) {
      xcur = xoff;
      scrt = scr1;
      for (p = 0; p < width; p++) {
      *scrt = ((*scrt & ~(1 << xcur))
             | (BGR233ToPixel[*(buf++)] << xcur));

      if (xcur-- == 0) {
        xcur = 7;
        scrt++;
      }
      }
      scr1 += fbwb;
    }
    break;

  case 8:
    for (q = 0; q < height; q++) {
      for (p = 0; p < width; p++) {
      *(scr8++) = BGR233ToPixel[*(buf++)];
      }
      scr8 += si.framebufferWidth - width;
    }
    break;

  case 16:
    for (q = 0; q < height; q++) {
      for (p = 0; p < width; p++) {
      *(scr16++) = BGR233ToPixel[*(buf++)];
      }
      scr16 += si.framebufferWidth - width;
    }
    break;

  case 32:
    for (q = 0; q < height; q++) {
      for (p = 0; p < width; p++) {
      *(scr32++) = BGR233ToPixel[*(buf++)];
      }
      scr32 += si.framebufferWidth - width;
    }
    break;
  }
}

/*
 * FillRectangle8.
 */

void
FillRectangle8(CARD8 fg, int x, int y, int width, int height)
{
  SoftCursorState s;

  if (!CheckRectangle(x, y, width, height))
    return;

  s = getSoftCursorState(x, y, width, height);
  if (s == SOFTCURSOR_PART_UNDER)
    undrawCursor();

  if (!appData.useBGR233) {
    int h;
    int widthInBytes = width * visbpp / 8;
    int scrWidthInBytes = image->bytes_per_line;

    char *scr = (image->data + y * scrWidthInBytes
             + x * visbpp / 8);

    for (h = 0; h < height; h++) {
      memset(scr, fg, widthInBytes);
      scr += scrWidthInBytes;
    }
  } else {
    FillRectangleBGR233(fg, x, y, width, height);
  }

  if (s != SOFTCURSOR_UNAFFECTED)
    drawCursor();
}

/*
 * FillRectangleBGR233.
 */

static void
FillRectangleBGR233(CARD8 fg, int x, int y, int width, int height)
{
  int p, q;
  int xoff = 7 - (x & 7);
  int xcur;
  int fbwb = si.framebufferWidth / 8;
  CARD8 *scr1 = ((CARD8 *)image->data) + y * fbwb + x / 8;
  CARD8 *scrt;
  CARD8 *scr8 = ((CARD8 *)image->data) + y * si.framebufferWidth + x;
  CARD16 *scr16 = ((CARD16 *)image->data) + y * si.framebufferWidth + x;
  CARD32 *scr32 = ((CARD32 *)image->data) + y * si.framebufferWidth + x;

  unsigned long fg233 = BGR233ToPixel[fg];

  switch (visbpp) {

    /* thanks to Chris Hooper for single bpp support */

  case 1:
    for (q = 0; q < height; q++) {
      xcur = xoff;
      scrt = scr1;
      for (p = 0; p < width; p++) {
      *scrt = ((*scrt & ~(1 << xcur))
             | (fg233 << xcur));

      if (xcur-- == 0) {
        xcur = 7;
        scrt++;
      }
      }
      scr1 += fbwb;
    }
    break;

  case 8:
    for (q = 0; q < height; q++) {
      for (p = 0; p < width; p++) {
      *(scr8++) = fg233;
      }
      scr8 += si.framebufferWidth - width;
    }
    break;

  case 16:
    for (q = 0; q < height; q++) {
      for (p = 0; p < width; p++) {
      *(scr16++) = fg233;
      }
      scr16 += si.framebufferWidth - width;
    }
    break;

  case 32:
    for (q = 0; q < height; q++) {
      for (p = 0; p < width; p++) {
      *(scr32++) = fg233;
      }
      scr32 += si.framebufferWidth - width;
    }
    break;
  }
}

/*
 * FillRectangle16
 */

void
FillRectangle16(CARD16 fg, int x, int y, int width, int height)
{
  int i, h;
  int scrWidthInBytes = image->bytes_per_line;
  
  char *scr = (image->data + y * scrWidthInBytes
             + x * visbpp / 8);
  CARD16 *scr16;
  SoftCursorState s;

  if (!CheckRectangle(x, y, width, height))
    return;

  s = getSoftCursorState(x, y, width, height);
  if (s == SOFTCURSOR_PART_UNDER)
    undrawCursor();

  for (h = 0; h < height; h++) {
    scr16 = (CARD16*) scr;
    for (i = 0; i < width; i++)
      scr16[i] = fg;
    scr += scrWidthInBytes;
  }

  if (s != SOFTCURSOR_UNAFFECTED)
    drawCursor();
}

/*
 * FillRectangle32
 */

void
FillRectangle32(CARD32 fg, int x, int y, int width, int height)
{
  int i, h;
  int scrWidthInBytes = image->bytes_per_line;
  SoftCursorState s;
  
  char *scr = (image->data + y * scrWidthInBytes
             + x * visbpp / 8);
  CARD32 *scr32;

  if (!CheckRectangle(x, y, width, height))
    return;

  s = getSoftCursorState(x, y, width, height);
  if (s == SOFTCURSOR_PART_UNDER)
    undrawCursor();

  for (h = 0; h < height; h++) {
    scr32 = (CARD32*) scr;
    for (i = 0; i < width; i++)
      scr32[i] = fg;
    scr += scrWidthInBytes;
  }

  if (s != SOFTCURSOR_UNAFFECTED)
    drawCursor();
}

/*
 * CopyDataFromScreen.
 */

void
CopyDataFromScreen(char *buf, int x, int y, int width, int height)
{
  int widthInBytes = width * visbpp / 8;
  int scrWidthInBytes = image->bytes_per_line;
  char *src = (image->data + y * scrWidthInBytes
             + x * visbpp / 8);
  int h;

  if (!CheckRectangle(x, y, width, height))
    return;

  for (h = 0; h < height; h++) {
    memcpy(buf, src, widthInBytes);
    src += scrWidthInBytes;
    buf += widthInBytes;
  }
}

/*
 * CopyArea
 */

void
CopyArea(int srcX, int srcY, int width, int height, int x, int y)
{
  int widthInBytes = width * visbpp / 8;
  SoftCursorState sSrc, sDst;

  LockFramebuffer();
  sSrc = getSoftCursorState(srcX, srcY, width, height);
  sDst = getSoftCursorState(x, y, width, height);
  if ((sSrc != SOFTCURSOR_UNAFFECTED) ||
      (sDst == SOFTCURSOR_PART_UNDER))
    undrawCursor();

  if ((srcY+height < y) || (y+height < srcY) ||
      (srcX+width  < x) || (x+width  < srcX)) {

    int scrWidthInBytes = image->bytes_per_line;
    char *src = (image->data + srcY * scrWidthInBytes
             + srcX * visbpp / 8);
    char *dst = (image->data + y * scrWidthInBytes
             + x * visbpp / 8);
    int h;

    if (!CheckRectangle(srcX, srcY, width, height)) {
      UnlockFramebuffer();
      return;
    }
    if (!CheckRectangle(x, y, width, height)) {
      UnlockFramebuffer();
      return;
    }
      
    for (h = 0; h < height; h++) {
      memcpy(dst, src, widthInBytes);
      src += scrWidthInBytes;
      dst += scrWidthInBytes;
    }
  }
  else { 
    char *buf = malloc(widthInBytes*height);
    if (!buf) {
      UnlockFramebuffer();
      fprintf(stderr, "Out of memory, CopyArea impossible\n");
      return;
    }
    CopyDataFromScreen(buf, srcX, srcY, width, height);
    CopyDataToScreenRaw(buf, x, y, width, height);
    free(buf);
  }
  if ((sSrc != SOFTCURSOR_UNAFFECTED) ||
      (sDst != SOFTCURSOR_UNAFFECTED))
    drawCursor();
  UnlockFramebuffer();
  SyncScreenRegion(x, y, width, height);
}

void SyncScreenRegion(int x, int y, int width, int height) {
  int dx, dy, dw, dh; 

  if (zoomActive) {
    Surface src, dest;
    src.w = si.framebufferWidth;
    src.h = si.framebufferHeight;
    src.pitch = image->bytes_per_line;
    src.pixels = image->data;
    src.BytesPerPixel = visbpp / 8;
    dest.w = zoomWidth;
    dest.h = zoomHeight;
    dest.pitch = zoomImage->bytes_per_line;
    dest.pixels = zoomImage->data;
    dest.BytesPerPixel = visbpp / 8;
    ZoomSurfaceSrcCoords(x, y, width, height, &dx, &dy, &dw, &dh, &src, &dest);
  }
  else {
    dx = x; dy = y;
    dw = width; dh = height;
  }
  DrawScreenRegion(dx, dy, dw, dh);
}

void SyncScreenRegionX11Thread(int x, int y, int width, int height) {
  int dx, dy, dw, dh; 

  if (zoomActive) {
    Surface src, dest;
    src.w = si.framebufferWidth;
    src.h = si.framebufferHeight;
    src.pitch = image->bytes_per_line;
    src.pixels = image->data;
    src.BytesPerPixel = visbpp / 8;
    dest.w = zoomWidth;
    dest.h = zoomHeight;
    dest.pitch = zoomImage->bytes_per_line;
    dest.pixels = zoomImage->data;
    dest.BytesPerPixel = visbpp / 8;
    ZoomSurfaceSrcCoords(x, y, width, height, &dx, &dy, &dw, &dh, &src, &dest);
  }
  else {
    dx = x; dy = y;
    dw = width; dh = height;
  }
  DrawAnyScreenRegionX11Thread(dx, dy, dw, dh);
}

/*
 * ToplevelInitBeforeRealization sets the title, geometry and other resources
 * on the toplevel window.
 */

void
ToplevelInit()
{
  dpyWidth = WidthOfScreen(DefaultScreenOfDisplay(dpy));
  dpyHeight = HeightOfScreen(DefaultScreenOfDisplay(dpy));
}

/*
 * Cleanup - perform shm cleanup operations prior to exiting.
 */

void
Cleanup()
{
  if (useShm || useZoomShm)
    ShmCleanup();
}

void
ShmCleanup()
{
  fprintf(stderr,"ShmCleanup called\n");
  if (needShmCleanup) {
    shmdt(shminfo.shmaddr);
    shmctl(shminfo.shmid, IPC_RMID, 0);
    needShmCleanup = False;
  }
  if (needZoomShmCleanup) {
    shmdt(zoomshminfo.shmaddr);
    shmctl(zoomshminfo.shmid, IPC_RMID, 0);
    needZoomShmCleanup = False;
  }
}

static int
ShmCreationXErrorHandler(Display *d, XErrorEvent *e)
{
  caughtShmError = True;
  return 0;
}

XImage *
CreateShmImage()
{
  XImage *_image;
  XErrorHandler oldXErrorHandler;

  if (!XShmQueryExtension(dpy))
    return NULL;

  _image = XShmCreateImage(dpy, vis, visdepth, ZPixmap, NULL, &shminfo,
                    si.framebufferWidth, si.framebufferHeight);
  if (!_image) return NULL;

  shminfo.shmid = shmget(IPC_PRIVATE,
                   _image->bytes_per_line * _image->height,
                   IPC_CREAT|0777);

  if (shminfo.shmid == -1) {
    XDestroyImage(_image);
    return NULL;
  }

  shminfo.shmaddr = _image->data = shmat(shminfo.shmid, 0, 0);

  if (shminfo.shmaddr == (char *)-1) {
    XDestroyImage(_image);
    shmctl(shminfo.shmid, IPC_RMID, 0);
    return NULL;
  }

  shminfo.readOnly = True;

  oldXErrorHandler = XSetErrorHandler(ShmCreationXErrorHandler);
  XShmAttach(dpy, &shminfo);
  XSync(dpy, False);
  XSetErrorHandler(oldXErrorHandler);

  if (caughtShmError) {
    XDestroyImage(_image);
    shmdt(shminfo.shmaddr);
    shmctl(shminfo.shmid, IPC_RMID, 0);
    return NULL;
  }

  needShmCleanup = True;

  fprintf(stderr,"Using shared memory PutImage\n");

  return _image;
}

void undrawCursor() {
  int x, y, w, h;
  
  if ((imageIndex < 0) || !savedArea)
    return;

  getBoundingRectCursor(cursorX, cursorY, imageIndex,
                  &x, &y, &w, &h);

  if ((w < 1) || (h < 1))
    return;

  CopyDataToScreenRaw(savedArea, x, y, w, h);
  discardCursorSavedArea();
}

static void drawCursorImage() {
  int x, y, w, h, pw, pixelsLeft, processingMask;
  int skipLeft, skipRight;
  PointerImage *pi = &pointerImages[imageIndex];
  CARD8 *img = (CARD8*) pi->image;
  CARD8 *imgEnd = &img[pi->len];
  CARD8 *fb;

  /* check whether the source image has ended (image broken) */
#define CHECK_IMG(x) if (&img[x] > imgEnd) goto imgError

/* check whether the end of the framebuffer has been reached (last line) */
#define CHECK_END()  if ((wl == 0) && (h == 1)) return

/* skip x pixels in the source (x must be < pixelsLeft!) */
#define SKIP_IMG(x)  if ((x > 0) && !processingMask) { \
      CHECK_END();   \
      img += pw * x; \
      CHECK_IMG(0);  \
   }

/* skip x pixels in source and destination */
#define SKIP_PIXELS(x) { int wl = x; \
    while (pixelsLeft <= wl) { \
      wl -= pixelsLeft;        \
      SKIP_IMG(pixelsLeft);    \
      CHECK_END();             \
      pixelsLeft = *(img++);   \
      CHECK_IMG(0);            \
      processingMask = processingMask ? 0 : 1; \
    }                          \
    pixelsLeft -= wl;          \
    SKIP_IMG(wl);              \
  }

  if (!img)
    return;

  x = cursorX - pi->hotX;
  y = cursorY - pi->hotY;
  w = pi->w;
  h = pi->h;

  if (!rectsIntersect(x, y, w, h, 
                  0, 0, si.framebufferWidth, si.framebufferHeight)) {
    fprintf(stderr, "intersect abort\n");
    return;
  }

  pw = myFormat.bitsPerPixel / 8;
  processingMask = 1;
  pixelsLeft = *(img++);

/* at this point everything is initialized for the macros */

  /* skip/clip bottom lines */
  if ((y+h) > si.framebufferHeight) 
    h = si.framebufferHeight - y;

  /* Skip invisible top lines */
  while (y < 0) {
    SKIP_PIXELS(w);
    y++;
    h--;
  }

  /* calculate left/right clipping */
  if (x < 0) {
    skipLeft = -x;
    w += x;
    x = 0;
  }
  else
    skipLeft = 0;

  if ((x+w) > si.framebufferWidth) {
    skipRight = (x+w) - si.framebufferWidth;
    w = si.framebufferWidth - x;
  }
  else
    skipRight = 0;

  fb = (CARD8*) image->data + y * image->bytes_per_line + x * visbpp / 8;

  /* Paint the thing */
  while (h > 0) {
    SKIP_PIXELS(skipLeft);

    {
      CARD8 *fbx = fb;
      int wl = w;
      while (pixelsLeft <= wl) {
      wl -= pixelsLeft;
      if ((pixelsLeft > 0) && !processingMask) {
        int pl = pw * pixelsLeft;
        CHECK_IMG(pl);
        if (!appData.useBGR233)
          memcpy(fbx, img, pl);
        else
          bgr233cpy(fbx, img, pixelsLeft);
        img += pl;
      }

        CHECK_END();
      fbx += pixelsLeft * visbpp / 8;
      pixelsLeft = *(img++);

      CHECK_IMG(0);
      processingMask = processingMask ? 0 : 1;
      }
      pixelsLeft -= wl;
      if ((wl > 0) && !processingMask) {
      int pl = pw * wl;
      CHECK_IMG(pl);
      if (!appData.useBGR233)
        memcpy(fbx, img, pl);
      else
        bgr233cpy(fbx, img, wl);
      img += pl;
      }
    }

    SKIP_PIXELS(skipRight);
    fb += image->bytes_per_line;
    h--;
  }
  return;

imgError:
  fprintf(stderr, "Error in softcursor image %d\n", imageIndex);
  pointerImages[imageIndex].set = 0;
}

static void discardCursorSavedArea() {
  if (savedArea)
    free(savedArea);
  savedArea = 0;
}

static void saveCursorSavedArea() {
  int x, y, w, h;

  if (imageIndex < 0)
    return;
  getBoundingRectCursor(cursorX, cursorY, imageIndex,
                  &x, &y, &w, &h);
  if ((w < 1) || (h < 1))
    return;
  discardCursorSavedArea();
  savedArea = malloc(h*image->bytes_per_line);
  if (!savedArea) {
    fprintf(stderr,"malloc failed, saving cursor not possible\n");
    exit(1);
  }
  CopyDataFromScreen(savedArea, x, y, w, h);  
}

void drawCursor() {
  saveCursorSavedArea();
  drawCursorImage();
}

void getBoundingRectCursor(int cx, int cy, int _imageIndex,
                     int *x, int *y, int *w, int *h) {
  int nx, ny, nw, nh;

  if ((_imageIndex < 0) || !pointerImages[_imageIndex].set) {
    *x = 0;
    *y = 0;
    *w = 0;
    *h = 0;
    return;
  }

  nx = cx - pointerImages[_imageIndex].hotX;
  ny = cy - pointerImages[_imageIndex].hotY;
  nw = pointerImages[_imageIndex].w;
  nh = pointerImages[_imageIndex].h;
  if (nx < 0) {
    nw += nx;
    nx = 0;
  }
  if (ny < 0) {
    nh += ny;
    ny = 0;
  }
  if ((nx+nw) > si.framebufferWidth)
    nw = si.framebufferWidth - nx;
  if ((ny+nh) > si.framebufferHeight)
    nh = si.framebufferHeight - ny;
  if ((nw <= 0) || (nh <= 0)) {
    *x = 0;
    *y = 0;
    *w = 0;
    *h = 0;
    return;
  }

  *x = nx;
  *y = ny;
  *w = nw;
  *h = nh;
}

static SoftCursorState getSoftCursorState(int x, int y, int w, int h) {
  int cx, cy, cw, ch;

  if (imageIndex < 0)
    return SOFTCURSOR_UNAFFECTED;

  getBoundingRectCursor(cursorX, cursorY, imageIndex,
                  &cx, &cy, &cw, &ch);

  if ((cw == 0) || (ch == 0))
    return SOFTCURSOR_UNAFFECTED;

  if (!rectsIntersect(x, y, w, h, cx, cy, cw, ch))
    return SOFTCURSOR_UNAFFECTED;
  if (rectContains(x, y, w, h, cx, cy, cw, ch))
    return SOFTCURSOR_UNDER;
  else
    return SOFTCURSOR_PART_UNDER;
}

int rectsIntersect(int x, int y, int w, int h, 
               int x2, int y2, int w2, int h2) {
  if (x2 >= (x+w))
    return 0;
  if (y2 >= (y+h))
    return 0;
  if ((x2+w2) <= x)
    return 0;
  if ((y2+h2) <= y)
    return 0;
  return 1;
}

int rectContains(int outX, int outY, int outW, int outH, 
             int inX, int inY, int inW, int inH) {
  if (inX < outX)
    return 0;
  if (inY < outY)
    return 0;
  if ((inX+inW) > (outX+outW))
    return 0;
  if ((inY+inH) > (outY+outH))
    return 0;
  return 1;
}

void rectsJoin(int *nx1, int *ny1, int *nw1, int *nh1, 
             int x2, int y2, int w2, int h2) {
  int ox, oy, ow, oh;
  ox = *nx1;
  oy = *ny1;
  ow = *nw1;
  oh = *nh1;
  
  if (x2 < ox) {
    ow += ox - x2;
    ox = x2;
  }
  if (y2 < oy) {
    oh += oy - y2;
    oy = y2;
  }
  if ((x2+w2) > (ox+ow))
    ow = (x2+w2) - ox;
  if ((y2+h2) > (oy+oh))
    oh = (y2+h2) - oy;

  *nx1 = ox;
  *ny1 = oy;
  *nw1 = ow;
  *nh1 = oh;
}

XImage *
CreateShmZoomImage()
{
  XImage *_image;
  XErrorHandler oldXErrorHandler;

  if (!XShmQueryExtension(dpy))
    return NULL;

  _image = XShmCreateImage(dpy, vis, visdepth, ZPixmap, NULL, &zoomshminfo,
                    si.framebufferWidth, si.framebufferHeight);
  if (!_image) return NULL;

  zoomshminfo.shmid = shmget(IPC_PRIVATE,
                       _image->bytes_per_line * _image->height,
                       IPC_CREAT|0777);

  if (zoomshminfo.shmid == -1) {
    XDestroyImage(_image);
    return NULL;
  }

  zoomshminfo.shmaddr = _image->data = shmat(zoomshminfo.shmid, 0, 0);

  if (zoomshminfo.shmaddr == (char *)-1) {
    XDestroyImage(_image);
    shmctl(zoomshminfo.shmid, IPC_RMID, 0);
    return NULL;
  }

  zoomshminfo.readOnly = True;

  oldXErrorHandler = XSetErrorHandler(ShmCreationXErrorHandler);
  XShmAttach(dpy, &zoomshminfo);
  XSync(dpy, False);
  XSetErrorHandler(oldXErrorHandler);

  if (caughtZoomShmError) {
    XDestroyImage(_image);
    shmdt(zoomshminfo.shmaddr);
    shmctl(zoomshminfo.shmid, IPC_RMID, 0);
    return NULL;
  }

  needZoomShmCleanup = True;

  fprintf(stderr,"Using shared memory PutImage\n");

  return _image;
}


/*
 * DrawZoomedScreenRegionX11Thread
 * Never call from any other desktop.c function, only for X11 thread
 */

void
DrawZoomedScreenRegionX11Thread(Window win, int zwidth, int zheight, 
                        int x, int y, int width, int height) {
  if (!image)
    return;                   
                        
  if (zwidth > si.framebufferWidth)
    zwidth = si.framebufferWidth;
  if (zheight > si.framebufferHeight)
    zheight = si.framebufferHeight;
  
  if (!zoomActive) {
    ZoomInit();
    zoomActive = True;
  }
  
  if ((zoomWidth != zwidth) || (zoomHeight != zheight)) {
    Surface src, dest;

    zoomWidth = zwidth;
    zoomHeight = zheight;
  
    src.w = si.framebufferWidth;
    src.h = si.framebufferHeight;
    src.pitch = image->bytes_per_line;
    src.pixels = image->data;
    src.BytesPerPixel = visbpp / 8;
    dest.w = zwidth;
    dest.h = zheight;
    dest.pitch = zoomImage->bytes_per_line;
    dest.pixels = zoomImage->data;
    dest.BytesPerPixel = visbpp / 8;
    sge_transform(&src, &dest,  
              (float)dest.w/(float)src.w, (float)dest.h/(float)src.h,
              0, 0);

    if (useZoomShm) 
      XShmPutImage(dpy, win, gc, zoomImage, 0, 0, 0, 0, zwidth, zheight, False);
    else
      XPutImage(dpy, win, gc, zoomImage, 0, 0, 0, 0, zwidth, zheight);
    return;
  }

  if (useZoomShm) 
    XShmPutImage(dpy, win, gc, zoomImage, x, y, x, y, width, height, False);
  else
    XPutImage(dpy, win, gc, zoomImage, x, y, x, y, width, height);
}


static void
ZoomInit()
{
  if (zoomImage)
    return;

   zoomImage = CreateShmZoomImage(); 

  if (!zoomImage) {
    useZoomShm = False;
    zoomImage = XCreateImage(dpy, vis, visdepth, ZPixmap, 0, NULL,
                       si.framebufferWidth, si.framebufferHeight,
                       BitmapPad(dpy), 0);

    zoomImage->data = calloc(zoomImage->bytes_per_line * zoomImage->height, 1);
    if (!zoomImage->data) {
      fprintf(stderr,"malloc failed\n");
      exit(1);
    }
  }
}

static void transformZoomSrc(int six, int siy, int siw, int sih,
                       int *dix, int *diy, int *diw, int *dih,
                       int srcW, int dstW, int srcH, int dstH) {
  double sx, sy, sw, sh;
  double dx, dy, dw, dh;
  double wq, hq;

  sx = six; sy = siy;
  sw = siw; sh = sih;

  wq = ((double)dstW) / (double) srcW;
  hq = ((double)dstH) / (double) srcH;

  dx = sx * wq;
  dy = sy * hq;
  dw = sw * wq;
  dh = sh * hq;
  
  *dix = dx;
  *diy = dy;
  *diw = dw+(dx-(int)dx)+0.5;
  *dih = dh+(dy-(int)dy)+0.5;
}

static void transformZoomDst(int *six, int *siy, int *siw, int *sih,
                       int dix, int diy, int diw, int dih,
                       int srcW, int dstW, int srcH, int dstH) {
  double sx, sy, sw, sh;
  double dx, dy, dw, dh;
  double wq, hq;

  dx = dix; dy = diy;
  dw = diw; dh = dih;

  wq = ((double)dstW) / (double) srcW;
  hq = ((double)dstH) / (double) srcH;

  sx = dx / wq;
  sy = dy / hq;
  sw = dw / wq;
  sh = dh / hq;
  
  *six = sx;
  *siy = sy;
  *siw = sw+(sx-(int)sx)+0.5;
  *sih = sh+(sy-(int)sy)+0.5;
}


static void ZoomSurfaceSrcCoords(int six, int siy, int siw, int sih,
                         int *dix, int *diy, int *diw, int *dih,
                         Surface * src, Surface * dst)
{
  int dx, dy, dw, dh;
  int sx, sy, sw, sh;

  transformZoomSrc(six, siy, siw, sih,
               &dx, &dy, &dw, &dh,
               src->w, dst->w, src->h, dst->h);
  dx-=2;
  dy-=2;
  dw+=4;
  dh+=4;

  if (dx < 0)
    dx = 0;
  if (dy < 0)
    dy = 0;
  if (dx+dw > dst->w)
    dw = dst->w - dx;
  if (dy+dh > dst->h)
    dh = dst->h - dy;
  
  transformZoomDst(&sx, &sy, &sw, &sh,
               dx, dy, dw, dh,
               src->w, dst->w, src->h, dst->h);

  if (sx+sw > src->w)
    sw = src->w - sx;
  if (sy+sh > src->h)
    sh = src->h - sy;

  ZoomSurfaceCoords32(sx, sy, sw, sh, dx, dy, src, dst);

  *dix = dx;
  *diy = dy;
  *diw = dw;
  *dih = dh;
}

static void ZoomSurfaceCoords32(int sx, int sy, int sw, int sh,
                        int dx, int dy, 
                        Surface * src, Surface * dst)
{
  Surface s2;

  s2 = *src;
  s2.pixels = ((char*)s2.pixels) + (sx * s2.BytesPerPixel) + (sy * src->pitch);
  s2.w = sw;
  s2.h = sh;
  sge_transform(&s2, dst,  
            (float)dst->w/(float)src->w, (float)dst->h/(float)src->h,
            dx, dy);
}


#define sge_clip_xmin(pnt) 0
#define sge_clip_xmax(pnt) pnt->w
#define sge_clip_ymin(pnt) 0
#define sge_clip_ymax(pnt) pnt->h

/*==================================================================================
// Helper function to sge_transform()
// Returns the bounding box
//==================================================================================
*/
static void _calcRect(Surface *src, Surface *dst, float xscale, float yscale, 
                  Uint16 qx, Uint16 qy, 
                  Sint16 *xmin, Sint16 *ymin, Sint16 *xmax, Sint16 *ymax)
{
      Sint16 x, y, rx, ry;
      int i;
      
      /* Clip to src surface */
      Sint16 sxmin = sge_clip_xmin(src);
      Sint16 sxmax = sge_clip_xmax(src);
      Sint16 symin = sge_clip_ymin(src);
      Sint16 symax = sge_clip_ymax(src);
      Sint16 sx[5];
      Sint16 sy[4];
      
      /* We don't really need fixed-point here
       * but why not? */
      Sint32 ictx = (Sint32) (xscale * 8192.0);
      Sint32 icty = (Sint32) (yscale * 8192.0);

      sx[0] = sxmin;
      sx[1] = sxmax;
      sx[2] = sxmin;
      sx[3] = sxmax;
      sy[0] = symin;
      sy[1] = symax;
      sy[2] = symax;
      sy[3] = symin;

      /* Calculate the four corner points */
      for(i=0; i<4; i++){
            rx = sx[i];
            ry = sy[i];
            
            x = (Sint16)(((ictx*rx) >> 13) + qx);
            y = (Sint16)(((icty*ry) >> 13) + qy);
            
            
            if(i==0){
                  *xmax = *xmin = x;
                  *ymax = *ymin = y;
            }else{
                  if(x>*xmax)
                        *xmax=x;
                  else if(x<*xmin)
                        *xmin=x;
                        
                  if(y>*ymax)
                        *ymax=y;
                  else if(y<*ymin)
                        *ymin=y;
            }
      }
      
      /* Better safe than sorry...*/
      *xmin -= 1;
      *ymin -= 1;
      *xmax += 1;
      *ymax += 1;
      
      /* Clip to dst surface */
      if( !dst )
            return;
      if( *xmin < sge_clip_xmin(dst) )
            *xmin = sge_clip_xmin(dst);
      if( *xmax > sge_clip_xmax(dst) )
            *xmax = sge_clip_xmax(dst);
      if( *ymin < sge_clip_ymin(dst) )
            *ymin = sge_clip_ymin(dst);
      if( *ymax > sge_clip_ymax(dst) )
            *ymax = sge_clip_ymax(dst);
}


/*==================================================================================
** Scale by scale and place at position (qx,qy). 
** 
**
** Developed with the help from Terry Hancock (hancock@earthlink.net)
**
**==================================================================================*/
/* First we need some macros to handle different bpp
 *  I'm sorry about this... 
 */
#define TRANSFORM(UintXX, DIV) \
      Sint32 src_pitch=src->pitch/DIV; \
      Sint32 dst_pitch=dst->pitch/DIV; \
      UintXX *src_row = (UintXX *)src->pixels; \
      UintXX *dst_row; \
\
      for (y=ymin; y<ymax; y++){ \
            dy = y - qy; \
\
            sx = (Sint32)ctdx;  /* Compute source anchor points */ \
            sy = (Sint32)(cty*dy); \
\
            /* Calculate pointer to dst surface */ \
            dst_row = (UintXX *)dst->pixels + y*dst_pitch; \
\
            for (x=xmin; x<xmax; x++){ \
                  rx=(Sint16)(sx >> 13);  /* Convert from fixed-point */ \
                  ry=(Sint16)(sy >> 13); \
\
                  /* Make sure the source pixel is actually in the source image. */ \
                  if( (rx>=sxmin) && (rx<sxmax) && (ry>=symin) && (ry<symax) ) \
                        *(dst_row + x) = *(src_row + ry*src_pitch + rx); \
\
                  sx += ctx;  /* Incremental transformations */ \
            } \
      }
      
      
/* Interpolated transform */
#define TRANSFORM_AA(UintXX, DIV) \
      Sint32 src_pitch=src->pitch/DIV; \
      Sint32 dst_pitch=dst->pitch/DIV; \
      UintXX *src_row = (UintXX *)src->pixels; \
      UintXX *dst_row; \
      UintXX c1, c2, c3, c4;\
      Uint32 R, G, B, A=0; \
      UintXX Rmask = image->red_mask;\
        UintXX Gmask = image->green_mask;\
        UintXX Bmask = image->blue_mask;\
        UintXX Amask = 0;\
      Uint32 wx, wy;\
      Uint32 p1, p2, p3, p4;\
\
      /* 
      *  Interpolation:
      *  We calculate the distances from our point to the four nearest pixels, d1..d4.
      *  d(a,b) = sqrt(a²+b²) ~= 0.707(a+b)  (Pythagoras (Taylor) expanded around (0.5;0.5))
      *  
      *    1  wx 2
      *     *-|-*  (+ = our point at (x,y))
      *     | | |  (* = the four nearest pixels)
      *  wy --+ |  wx = float(x) - int(x)
      *     |   |  wy = float(y) - int(y)
      *     *---*
      *    3     4
      *  d1 = d(wx,wy)  d2 = d(1-wx,wy)  d3 = d(wx,1-wy)  d4 = d(1-wx,1-wy)
      *  We now want to weight each pixels importance - it's vicinity to our point:
      *  w1=d4  w2=d3  w3=d2  w4=d1  (Yes it works... just think a bit about it)
      *
      *  If the pixels have the colors c1..c4 then our point should have the color
      *  c = (w1*c1 + w2*c2 + w3*c3 + w4*c4)/(w1+w2+w3+w4)   (the weighted average)
      *  but  w1+w2+w3+w4 = 4*0.707  so we might as well write it as
      *  c = p1*c1 + p2*c2 + p3*c3 + p4*c4  where  p1..p4 = (w1..w4)/(4*0.707)
      *
      *  But p1..p4 are fixed point so we can just divide the fixed point constant!
      *  8192/(4*0.71) = 2897  and we can skip 0.71 too (the division will cancel it everywhere)
      *  8192/4 = 2048
      *
      *  020102: I changed the fixed-point representation for the variables in the weighted average
      *          to 24.7 to avoid problems with 32bit colors. Everything else is still 18.13. This
      *          does however not solve the problem with 32bit RGBA colors... 
      */\
\
      Sint32 one = 2048>>6;   /* 1 in Fixed-point */ \
      Sint32 two = 2*2048>>6; /* 2 in Fixed-point */ \
\
      for (y=ymin; y<ymax; y++){ \
            dy = y - qy; \
\
            sx = (Sint32)(ctdx);  /* Compute source anchor points */ \
            sy = (Sint32)(cty*dy); \
\
            /* Calculate pointer to dst surface */ \
            dst_row = (UintXX *)dst->pixels + y*dst_pitch; \
\
            for (x=xmin; x<xmax; x++){ \
                  rx=(Sint16)(sx >> 13);  /* Convert from fixed-point */ \
                  ry=(Sint16)(sy >> 13); \
\
                  /* Make sure the source pixel is actually in the source image. */ \
                  if( (rx>=sxmin) && (rx+1<sxmax) && (ry>=symin) && (ry+1<symax) ){ \
                        wx = (sx & 0x00001FFF) >>8;  /* (float(x) - int(x)) / 4 */ \
                        wy = (sy & 0x00001FFF) >>8;\
\
                        p4 = wx+wy;\
                        p3 = one-wx+wy;\
                        p2 = wx+one-wy;\
                        p1 = two-wx-wy;\
\
                        c1 = *(src_row + ry*src_pitch + rx);\
                        c2 = *(src_row + ry*src_pitch + rx+1);\
                        c3 = *(src_row + (ry+1)*src_pitch + rx);\
                        c4 = *(src_row + (ry+1)*src_pitch + rx+1);\
\
                        /* Calculate the average */\
                        R = ((p1*(c1 & Rmask) + p2*(c2 & Rmask) + p3*(c3 & Rmask) + p4*(c4 & Rmask))>>7) & Rmask;\
                        G = ((p1*(c1 & Gmask) + p2*(c2 & Gmask) + p3*(c3 & Gmask) + p4*(c4 & Gmask))>>7) & Gmask;\
                        B = ((p1*(c1 & Bmask) + p2*(c2 & Bmask) + p3*(c3 & Bmask) + p4*(c4 & Bmask))>>7) & Bmask;\
                        if(Amask)\
                              A = ((p1*(c1 & Amask) + p2*(c2 & Amask) + p3*(c3 & Amask) + p4*(c4 & Amask))>>7) & Amask;\
                        \
                        *(dst_row + x) = R | G | B | A;\
                  } \
                  sx += ctx;  /* Incremental transformations */ \
            } \
      } 

void sge_transform(Surface *src, Surface *dst, float xscale, float yscale, Uint16 qx, Uint16 qy)
{
      Sint32 dy, sx, sy;
      Sint16 x, y, rx, ry;
      Rect r;

      Sint32 ctx, cty;
      Sint16 xmin, xmax, ymin, ymax;
      Sint16 sxmin, sxmax, symin, symax;
      Sint32 dx, ctdx;


      /* Here we use 18.13 fixed point integer math
      // Sint32 should have 31 usable bits and one for sign
      // 2^13 = 8192
      */

      /* Check scales */
      Sint32 maxint = (Sint32)(pow(2, sizeof(Sint32)*8 - 1 - 13));  /* 2^(31-13) */

      r.x = r.y = r.w = r.h = 0;
      
      if( xscale == 0 || yscale == 0)
            return;
            
      if( 8192.0/xscale > maxint )
            xscale =  (float)(8192.0/maxint);
      else if( 8192.0/xscale < -maxint )
            xscale =  (float)(-8192.0/maxint);  
            
      if( 8192.0/yscale > maxint )
            yscale =  (float)(8192.0/maxint);
      else if( 8192.0/yscale < -maxint )
            yscale =  (float)(-8192.0/maxint);


      /* Fixed-point equivalents */
      ctx = (Sint32)(8192.0/xscale);
      cty = (Sint32)(8192.0/yscale);

      /* Compute a bounding rectangle */
      xmin=0; xmax=dst->w; ymin=0; ymax=dst->h;
      _calcRect(src, dst, xscale, yscale, 
              qx, qy, &xmin, &ymin, &xmax, &ymax);    

      /* Clip to src surface */
      sxmin = sge_clip_xmin(src);
      sxmax = sge_clip_xmax(src);
      symin = sge_clip_ymin(src);
      symax = sge_clip_ymax(src);

      /* Some terms in the transform are constant */
      dx = xmin - qx;
      ctdx = ctx*dx;
      
      /* Use the correct bpp */
      if( src->BytesPerPixel == dst->BytesPerPixel){
            switch( src->BytesPerPixel ){
                  case 1: { /* Assuming 8-bpp */
                        TRANSFORM(Uint8, 1)
                  }
                  break;
                  case 2: { /* Probably 15-bpp or 16-bpp */
                        TRANSFORM_AA(Uint16, 2)
                  }
                  break;
                  case 4: { /* Probably 32-bpp */
                        TRANSFORM_AA(Uint32, 4)
                  }
                  break;
            }
      }
}

void freeDesktopResources() {
  Cleanup();
  if (image) {
    XDestroyImage(image);
  }
  if (zoomImage) {
    XDestroyImage(zoomImage);
  }
  if (savedArea)
    free(savedArea);

  if (gcInited) {
    XFreeGC(dpy, gc);
    XFreeGC(dpy, srcGC);
    XFreeGC(dpy, dstGC);
  }

  caughtShmError = False;
  needShmCleanup = False;
  caughtZoomShmError = False;
  needZoomShmCleanup = False;
  gcInited = False;
  image = NULL;
  useShm = True;
  zoomActive = False;
  zoomImage = NULL;
  useZoomShm = True;
  savedArea = NULL;
}


/*
 * ColorRectangle32
 * Only used for debugging / visualizing output
 */
/*
static void
ColorRectangle32(XImage *img, CARD32 fg, int x, int y, int width, int height)
{
  int i, h;
  int scrWidthInBytes = img->bytes_per_line;
  char *scr;
  CARD32 *scr32;

  if ((!img) || (!img->data))
    return;

  scr = (img->data + y * scrWidthInBytes + x * 4);
    
  if (!CheckRectangle(x, y, width, height))
    return;

  for (h = 0; h < height; h++) {
    scr32 = (CARD32*) scr;
    for (i = 0; i < width; i++) {
      CARD32 n = 0;
      CARD32 p = scr32[i];
      if (0xff & fg)
      n |= (((    0xff & p)+(    0xff & fg)) >> 2) & 0xff;
      else
      n |= (0xff & p);
      if (0xff00 & fg)
      n |= (((  0xff00 & p)+(  0xff00 & fg)) >> 2) & 0xff00;
      else
      n |= (0xff00 & p);
      if (0xff0000 & fg)
      n |= (((0xff0000 & p)+(0xff0000 & fg)) >> 2) & 0xff0000;
      else
      n |= (0xff0000 & p);
      scr32[i] = n;
    }
    scr += scrWidthInBytes;
  }
}
*/


Generated by  Doxygen 1.6.0   Back to index