Logo Search packages:      
Sourcecode: x2vnc version File versions

x.c

/*
 *  Copyright (C) 1997, 1998 Olivetti & Oracle Research Laboratory
 *
 *  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.
 *
 *  Seriously modified by Fredrik Hübinette <hubbe@hubbe.net>
 */

/*
 * x.c - functions to deal with X display.
 */

#include <sys/types.h>
#include <unistd.h>
#include <x2vnc.h>
#include <X11/X.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#include <X11/cursorfont.h>
#include <X11/keysym.h>

#ifdef HAVE_XIDLE
#include "xidle.h"

int server_has_xidle=0;
#endif

#ifdef HAVE_MIT_SCREENSAVER
#include <X11/extensions/scrnsaver.h>

int server_has_mitscreensaver=0;
#endif

#ifdef HAVE_XINERAMA
#include <X11/extensions/Xinerama.h>
#endif

Display *dpy;
static Window topLevel;
static int topLevelWidth, topLevelHeight;

static Atom wmProtocols, wmDeleteWindow, wmState;
static Bool modifierPressed[256];

static Bool HandleTopLevelEvent(XEvent *ev);
static Bool HandleRootEvent(XEvent *ev);
static int displayWidth, displayHeight;
static int x_offset=0, y_offset=0;
static int grabbed;
Cursor  grabCursor;

#define XROOT(E) ((E).x_root - x_offset)
#define YROOT(E) ((E).y_root - y_offset)

enum edge_enum edge = EDGE_EAST;
int edge_width=1;
int restingx=-1;
int restingy=-1;
int emulate_wheel=0;
int emulate_nav=0;
int wheel_button_up=4;
int scroll_lines=1;
int mac_mode=0;
int hidden=0;
extern int debug;

int last_event_time = 0;

KeySym grabkeysym=XK_F12;
KeyCode grabkey;
int grabmod = ControlMask;

char *client_selection_text=0;
size_t client_selection_text_length=0;

int saved_xpos=-1;
int saved_ypos=-1;

int saved_remote_xpos=-1;
int saved_remote_ypos=-1;

long grab_timeout=0;
long grab_timeout_delay=590;
#define SET_GRAB_TIMEOUT() grab_timeout= time(0) + grab_timeout_delay


/* We must do our own desktop handling */
Atom current_desktop_atom;
Atom number_of_desktops_atom;

int requested_desktop = -2;
int current_desktop = -2;
int current_number_of_desktops = -2;

int remote_is_locked = 0;

static void ungrabit(int x, int y, Window warpWindow);

typedef int(*xerrorhandler)(Display *, XErrorEvent *);

int warn_about_hotkey(Display *dpy, XErrorEvent *ev)
{
  grabkeysym=0;
  fprintf(stderr,"Warning: Failed to bind x2vnc hotkey, hotkey disabled.\n");
  return 0;
}

/*
 * This variable is true (1) if the mouse is on the same screen as the one
 * we're monitoring, or if there is only one screen on the X server.
 * - GRM
 */
static Bool mouseOnScreen;

/*
 * CreateXWindow.
 */

Bool CreateXWindow(void)
{
  XSetWindowAttributes attr;
  XEvent ev;
  char defaultGeometry[256];
  XSizeHints wmHints;
  XGCValues gcv;
  int i;
  int ew;
  
  Pixmap    nullPixmap;
  XColor    dummyColor;
  
  if (!(dpy = XOpenDisplay(displayname))) {
    fprintf(stderr,"%s: unable to open display %s\n",
          programName, XDisplayName(displayname));
    return False;
  }

  /*
   * check extensions
   */
#ifdef HAVE_XIDLE
 {
   int x,y;
   server_has_xidle=XidleQueryExtension(dpy, &x,&y);
 }
#endif

#ifdef HAVE_MIT_SCREENSAVER
 {
   int x,y;
   server_has_mitscreensaver=XScreenSaverQueryExtension(dpy, &x,&y);
   if(debug)
     fprintf(stderr,"MIT-SCREEN-SAVER = %d\n",server_has_mitscreensaver);
 }
#endif

  if(noblank
#ifdef HAVE_XIDLE
     && (!server_has_xidle)
#endif
#ifdef HAVE_MIT_SCREENSAVER
     && (!server_has_mitscreensaver)
#endif
    )
    fprintf(stderr,"-noblank option used, but no approperiate extensions found:\n"
          " x2vnc will keep the remote screen active at all times.\n");
  
  for (i = 0; i < 256; i++)
    modifierPressed[i] = False;
  
  /* Try to work out the geometry of the top-level window */
  
  displayWidth = WidthOfScreen(DefaultScreenOfDisplay(dpy));
  displayHeight = HeightOfScreen(DefaultScreenOfDisplay(dpy));
  
  saved_remote_xpos = displayWidth / 2;
  saved_remote_ypos = displayHeight / 2;
  
  if(restingy == -1)
  {
    restingy = si.framebufferHeight -2 - mac_mode;
    restingx = si.framebufferWidth -2 + mac_mode;
  }

#ifdef HAVE_XINERAMA
 {
   int x,y;
   if(XineramaQueryExtension(dpy,&x,&y) &&
      XineramaIsActive(dpy))
   {
     int pos,e;
     int num_heads;
     int bestpos=0;
     int besthead=0;
     XineramaScreenInfo *heads;

     if(heads=XineramaQueryScreens(dpy, &num_heads))
     {
       /* Loop over all heads and find whatever head
      * corresponds best with the edge the user has
      * selected. If the user selects "north", the
      * bigger screen will normally be selected.
      *
      * This is actually kind of stupid, it should really
      * use the biggest screen for off-screen stuff.
      */
       
       for(e=0;e<num_heads;e++)
       {
       switch(edge)
       {
         case EDGE_EAST:
           pos=heads[e].x_org + heads[e].width + (heads[e].height>>8);
           break;
         case EDGE_WEST:
           pos=1000-heads[e].x_org + (heads[e].height>>8);
           break;
         case EDGE_SOUTH:
           pos=heads[e].y_org + heads[e].height + (heads[e].width>>8);
           break;
         case EDGE_NORTH:
           pos=1000-heads[e].y_org + (heads[e].width>>8);
           break;
       }
       fprintf(stderr,"screen[%d] pos=%d\n",e,pos);

       if(pos > bestpos)
       {
         bestpos=pos;
         besthead=e;
       }
       }


       printf("Xinerama detected, x2vnc will use screen %d.\n",
            besthead+1);

       x_offset = heads[besthead].x_org;
       y_offset = heads[besthead].y_org;
       displayWidth = heads[besthead].width;
       displayHeight = heads[besthead].height;
#if 0
       fprintf(stderr,"[%d,%d-%d,%d]\n",x_offset,y_offset,
             displayWidth,displayHeight);
#endif
     }
     XFree(heads);
   }
 }
#endif
  ew=edge_width;
  if(!ew) ew=1;
  topLevelWidth=ew;
  topLevelHeight=ew;
  wmHints.x=x_offset;
  wmHints.y=y_offset;
  
  switch(edge)
  {
    case EDGE_EAST: wmHints.x=displayWidth-ew+x_offset;
    case EDGE_WEST: topLevelHeight=displayHeight;
      break;
      
    case EDGE_SOUTH: wmHints.y=displayHeight-ew+y_offset;
    case EDGE_NORTH: topLevelWidth=displayWidth;
      break;
  }
  
  wmHints.flags = PMaxSize | PMinSize |PPosition |PBaseSize;
  
  wmHints.min_width = topLevelWidth;
  wmHints.min_height = topLevelHeight;
  
  wmHints.max_width = topLevelWidth;
  wmHints.max_height = topLevelHeight;
  
  wmHints.base_width = topLevelWidth;
  wmHints.base_height = topLevelHeight;
  
  sprintf(defaultGeometry, "%dx%d+%d+%d",
        topLevelWidth, topLevelHeight,
        wmHints.x, wmHints.y);
  
  XWMGeometry(dpy, DefaultScreen(dpy), geometry, defaultGeometry, 0,
            &wmHints, &wmHints.x, &wmHints.y,
            &topLevelWidth, &topLevelHeight, &wmHints.win_gravity);
  
  /* Create the top-level window */
  
  attr.border_pixel = 0; /* needed to allow 8-bit cmap on 24-bit display -
                      otherwise we get a Match error! */
  attr.background_pixel = BlackPixelOfScreen(DefaultScreenOfDisplay(dpy));
  attr.event_mask = ( LeaveWindowMask|
                  StructureNotifyMask|
                  ButtonPressMask|
                  ButtonReleaseMask|
                  PointerMotionMask|
                  KeyPressMask|
                  KeyReleaseMask|
                  EnterWindowMask|
                  (resurface?VisibilityChangeMask:0) );
  
  attr.override_redirect=1;

  topLevel = XCreateWindow(dpy, DefaultRootWindow(dpy), wmHints.x, wmHints.y,
                     topLevelWidth, topLevelHeight, 0, CopyFromParent,
                     InputOutput, CopyFromParent,
                     (CWBorderPixel|
                      CWEventMask|
                      CWOverrideRedirect|
                      CWBackPixel),
                     &attr);

  current_desktop_atom = XInternAtom(dpy, "_NET_CURRENT_DESKTOP", False);
  number_of_desktops_atom = XInternAtom(dpy, "_NET_NUMBER_OF_DESKTOPS", False);

#if 1
  {
    Atom t = XInternAtom(dpy, "_NET_WM_WINDOW_DOCK", False);

    XChangeProperty(dpy,
                topLevel,
                XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False),
                XA_ATOM,
                32,
                PropModeReplace,
                (unsigned char *)&t,
                1);
  }
#endif


  wmHints.flags |= USPosition; /* try to force WM to place window */
  XSetWMNormalHints(dpy, topLevel, &wmHints);

  wmProtocols = XInternAtom(dpy, "WM_PROTOCOLS", False);
  wmDeleteWindow = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
  XSetWMProtocols(dpy, topLevel, &wmDeleteWindow, 1);
  
  XStoreName(dpy, topLevel, desktopName);

  if(edge_width)
    XMapRaised(dpy, topLevel);
  else
    hidden=1;
  
  /*
   * For multi-screen setups, we need to know if the mouse is on the 
   * screen.
   * - GRM
   */
  if (ScreenCount(dpy) > 1) {
    Window root, child;
    int root_x, root_y;
    int win_x, win_y;
    unsigned int keys_buttons;
    
    XSelectInput(dpy, DefaultRootWindow(dpy), PropertyChangeMask |
             EnterWindowMask | LeaveWindowMask | KeyPressMask);
    /* Cut buffer happens on screen 0 only. */
    if (DefaultRootWindow(dpy) != RootWindow(dpy, 0)) {
      XSelectInput(dpy, RootWindow(dpy, 0), PropertyChangeMask);
    }
    XQueryPointer(dpy, DefaultRootWindow(dpy), &root, &child,
              &root_x, &root_y, &win_x, &win_y, &keys_buttons);
    mouseOnScreen = root == DefaultRootWindow(dpy);
  } else {
    XSelectInput(dpy, DefaultRootWindow(dpy), PropertyChangeMask);
    mouseOnScreen = 1;
  }
  
  nullPixmap = XCreatePixmap(dpy, DefaultRootWindow(dpy), 1, 1, 1);
#if 0
  grabCursor = 0;
#else
  grabCursor = 
    XCreatePixmapCursor(dpy, nullPixmap, nullPixmap,
                  &dummyColor, &dummyColor, 0, 0);
#endif
  
  if(grabkeysym)
  {
    xerrorhandler prev_err_handler=XSetErrorHandler(warn_about_hotkey);
    grabkey=XKeysymToKeycode(dpy, grabkeysym);
#ifdef DEBUG
    fprintf(stderr,"Grabbing key = %d, modifiers = %x\n",grabkey,grabmod);
#endif
    XGrabKey(dpy, grabkey, grabmod,
           DefaultRootWindow(dpy),
           1, GrabModeSync, GrabModeSync);

    XSync(dpy, 0);

    if(grabkeysym)
    {
      /* Allow numlock */
      XGrabKey(dpy, grabkey, grabmod | Mod2Mask,
             DefaultRootWindow(dpy),
             1, GrabModeSync, GrabModeSync);
    }
    XSync(dpy, 0);

    XSetErrorHandler(prev_err_handler);
  }

  /* hide the cursor, so user won't get confused
     which keyboard has control */
  SendPointerEvent(restingx,restingy,0);

  
  return True;
}



/*
 * ShutdownX.
 */

void
ShutdownX()
{
  XCloseDisplay(dpy);
}


/*
 * check_idle()
 * Check how long the user has been idle using
 * various methods.... (in ms)
 */

Time check_idle(void)
{
#ifdef HAVE_XIDLE
  if(server_has_xidle)
  {
    Time ret = 0;
    XGetIdleTime(dpy, &ret);
    return ret;
  }
#endif

#if HAVE_MIT_SCREENSAVER
  if(server_has_mitscreensaver)
  {
    static XScreenSaverInfo* info = 0; 
  
    if (!info) info = XScreenSaverAllocInfo();
    
    XScreenSaverQueryInfo(dpy, DefaultRootWindow(dpy), info);
    return info->idle;
  }
#endif

  return 0;
}

void WiggleMouse(void)
{
  int tmpy=restingy > si.framebufferHeight /2 ? restingy -1 : restingy + 1;
  SendPointerEvent(restingx,tmpy,0);
  SendPointerEvent(restingx,restingy,0);
}


/*
 * HandleXEvents.
 */

Bool HandleXEvents(void)
{
  XEvent ev;

  if(grabbed)
  {
    if(grab_timeout_delay && time(0) > grab_timeout)
      ungrabit(-1, -1, DefaultRootWindow(dpy));
  }else{ /* grabbed */
    int remote_idle = time(0) - last_event_time;
    if(remote_idle > no_wakeup_delay)
      remote_is_locked=1;
#ifdef DEBUG
    fprintf(stderr,"IDLE=%d remote_idle=%d\n",
          check_idle(),remote_idle);
#endif
    if(noblank &&
       remote_idle > 45 &&
       !remote_is_locked &&
       check_idle() < 30000)
    {
      WiggleMouse();
    }
  }
  
  
  /* presumably XCheckMaskEvent is more efficient than XCheckIfEvent -GRM */
  while(XCheckIfEvent(dpy, &ev, AllXEventsPredicate, NULL))
  {
    if (ev.xany.window == topLevel)
    {
      if (!HandleTopLevelEvent(&ev))
      return False;
    }
    else if (ev.xany.window == DefaultRootWindow(dpy) ||
           ev.xany.window == RootWindow(dpy, 0)) {
      if (!HandleRootEvent(&ev))
      return False;
    }
    else if (ev.type == MappingNotify)
    {
      XRefreshKeyboardMapping(&ev.xmapping);
    }
  }

  return True;
}

#define EW (edge == EDGE_EAST || edge==EDGE_WEST)
#define NS (edge == EDGE_NORTH || edge==EDGE_SOUTH)
#define ES (edge == EDGE_EAST || edge==EDGE_SOUTH)
#define NS (edge == EDGE_NORTH || edge==EDGE_SOUTH)

static int enter_translate(int isedge, int width, int pos)
{
  if(!isedge) return pos;
  if(ES) return 0;
  return width-1;
}

static int leave_translate(int isedge, int width, int pos)
{
  if(!isedge) return pos;
  if(ES) return width-edge_width;
  return 0;
}


#define EDGE(X) ((X)?edge_width:0)

#define SCALEX(X) \
( ((X)-EDGE(edge==EDGE_WEST ))*(si.framebufferWidth-1 )/(displayWidth -1-EDGE(EW)) )

#define SCALEY(Y) \
( ((Y)-EDGE(edge==EDGE_NORTH))*(si.framebufferHeight-1)/(displayHeight-1-EDGE(NS)) )


static int sendpointerevent(int localx, int localy, int buttonmask)
{
  if(mac_mode && (buttonmask & (1<<2)))
  {
    buttonmask &=~ (1<<2);
    buttonmask |=~ (1<<0);
  }

  return SendPointerEvent(SCALEX(localx), SCALEY(localy), buttonmask);
}


void mapwindow(void)
{
  if(edge_width)
  {
    hidden=0;
    XMapRaised(dpy, topLevel);
  }
}

void hidewindow(void)
{
  hidden=1;
  XUnmapWindow(dpy, topLevel);
}

static void grabit(int x, int y, int state)
{
  Window selection_owner;

  if(hidden)
  {
    XMapRaised(dpy, topLevel);
    hidden=0;
  }

  XGrabPointer(dpy, topLevel, True,
             PointerMotionMask | ButtonPressMask | ButtonReleaseMask,
             GrabModeAsync, GrabModeAsync,
             None, grabCursor, CurrentTime);
  XGrabKeyboard(dpy, topLevel, True, 
            GrabModeAsync, GrabModeAsync,
            CurrentTime);
  XFlush(dpy);

  if(x > -1 && y > -1)
  {
    XWarpPointer(dpy,None, DefaultRootWindow(dpy),0,0,0,0,
             x_offset + x,
             y_offset + y);
    sendpointerevent(x, y, (state & 0x1f00) >> 8);
  }

  grabbed=1;
  mouseOnScreen = 1;

  selection_owner=XGetSelectionOwner(dpy, XA_PRIMARY);
/*   fprintf(stderr,"Selection owner: %lx\n",(long)selection_owner); */

  if(selection_owner != None && selection_owner != topLevel)
  {
    XConvertSelection(dpy,
                  XA_PRIMARY, XA_STRING, XA_CUT_BUFFER0,
                  topLevel, CurrentTime);
  }
  SET_GRAB_TIMEOUT();
}

static void ungrabit(int x, int y, Window warpWindow)
{
  int i;

  SendPointerEvent(restingx,restingy,0);
  if(x > -1 && y > -1 )
    XWarpPointer(dpy,None, warpWindow, 0,0,0,0, x_offset + x, y_offset + y);
  XUngrabKeyboard(dpy, CurrentTime);
  XUngrabPointer(dpy, CurrentTime);
  mouseOnScreen = warpWindow == DefaultRootWindow(dpy);
  XFlush(dpy);
  
  for (i = 255; i >= 0; i--)
  {
    if (modifierPressed[i]) {
      if (!SendKeyEvent(XKeycodeToKeysym(dpy, i, 0), False))
      return;
      modifierPressed[i]=False;
    }
  }

  /* this is a workaround for some older vnc servers which don't
   * send selection events unless you request a screen update
   * It is possible that this should be an command-line option,
   * but getting a one-pixel screen update should be reasonably
   * harmless.
   */
  SendFramebufferUpdateRequest(0,0,1,1,0);

  if(!edge_width) hidewindow();
  
  grabbed=0;
}

static void shortsleep(int usec)
{
  struct timeval timeout;
  timeout.tv_sec=0;
  timeout.tv_usec=usec;
  select(0,0,0,0,&timeout);
}


/*
 * HandleTopLevelEvent.
 */
static Bool HandleTopLevelEvent(XEvent *ev)
{
  Bool grab;
  int i;
  int x, y;
  
  int buttonMask;
  KeySym ks;
  static Atom COMPOUND_TEXT;
  /* Atom: requestor asks for a list of supported targets (GRM 24 Oct 2003) */
  static Atom TARGETS;

  
  SET_GRAB_TIMEOUT();

  switch (ev->type)
  {
    case SelectionRequest:
    {
      XEvent reply;

      XSelectionRequestEvent *req=& ev->xselectionrequest;

      reply.xselection.type=SelectionNotify;
      reply.xselection.display=req->display;
      reply.xselection.selection=req->selection;
      reply.xselection.requestor=req->requestor;
      reply.xselection.target=req->target;
      reply.xselection.time=req->time;
      reply.xselection.property=None;
      
      if(COMPOUND_TEXT == None)
      COMPOUND_TEXT = XInternAtom(dpy, "COMPOUND_TEXT", True);

      /* Initialize TARGETS atom if required (GRM 24 Oct 2003) */
      if(TARGETS == None) TARGETS = XInternAtom(dpy, "TARGETS", True);

      if(client_selection_text)
      {
      if(req->target == TARGETS)
      {
          /*
           * Requestor is asking what targets we support. Reply with what we
           * support (XA_STRING and COMPOUND_TEXT).
           *
           * (GRM 24 Oct 2003)
           */
        int ret;
          int targets[2];
        if(req->property == None)
          req->property = TARGETS;

          targets[0] = XA_STRING;
          targets[1] = COMPOUND_TEXT;
        XChangeProperty(dpy,
                    req->requestor,
                    req->property,
                    XA_ATOM,
                    8 * sizeof(int),
                    PropModeReplace,
                          (unsigned char *) targets,
                          2);

        reply.xselection.property=req->property;
        }
      else if(req->target == XA_STRING || req->target == COMPOUND_TEXT)
      {
        int ret;
        if(req->property == None)
          req->property = XA_CUT_BUFFER0;

#if 0
        fprintf(stderr,"Setting property %d on window %x to: %s (len=%d)\n",req->property, req->requestor, client_selection_text,client_selection_text_length);
#endif

        XChangeProperty(dpy,
                    req->requestor,
                    req->property,
                    XA_STRING,
                    8,
                    PropModeReplace,
                    client_selection_text,
                    client_selection_text_length);

        reply.xselection.property=req->property;
        
      }
        else if(debug)
        {
            /*
             * Requestor is asking for something we don't understand. Complain.
             * (GRM 24 Oct 2003)
             */
            char *name;
            name = XGetAtomName(dpy, req->target);
            fprintf(stderr, "Unknown property target request %s\n", name);
            XFree(name);
        }
      }
      XSendEvent(dpy, req->requestor, 0,0, &reply);
      XFlush (dpy);
    }
    return 1;

    case SelectionNotify:
    {
      Atom t;
      unsigned long len=0;
      unsigned long bytes_left=0;
      int format;
      unsigned char *data=0;
      int ret;

      ret=XGetWindowProperty(dpy,
                       topLevel,
                       XA_CUT_BUFFER0,
                       0,
                       0x100000,
                       1, /* delete */
                       XA_STRING,
                       &t,
                       &format,
                       &len,
                       &bytes_left,
                       &data);

      if(format == 8)
      SendClientCutText((char *)data, len);
#ifdef DEBUG
      fprintf(stderr,"GOT selection info: ret=%d type=%d fmt=%d len=%d bytes_left=%d %p '%s'\n",
      ret, (int)t,format,len,bytes_left,data,data);
#endif
      if(data) XFree(data);
                   
    }
    return 1;
      
    case VisibilityNotify:
      /*
       * I avoid resurfacing when the window becomes fully obscured, because
       * that *probably* means that somebody is running xlock.
       * Please tell me if you have a problem with this.
       * - Hubbe
       */
      if (ev->xvisibility.state == VisibilityPartiallyObscured &&
        resurface && !hidden)
      {
      static long last_resurface=0;
      long t=time(0);

      if(t == last_resurface)
        shortsleep(5000);

      last_resurface=t;
#ifdef DEBUG
      fprintf(stderr,"Raising window!\n");
#endif
      XRaiseWindow(dpy, topLevel);
      }
      return 1;

      /*
       * We don't need to worry about multi-screen here; that's handled
       * below, as a root event.
       * - GRM
       */
    case EnterNotify:
      if(!grabbed && ev->xcrossing.mode==NotifyNormal)
      {
      grabit(enter_translate(EW,displayWidth ,XROOT(ev->xcrossing)),
             enter_translate(NS,displayHeight,YROOT(ev->xcrossing)),
             ev->xcrossing.state);
      }
      return 1;
      
    case MotionNotify:
      
      if(grabbed)
      {
      int i, d;
        int x, y;
        Window warpWindow;
        while (XCheckTypedWindowEvent(dpy, topLevel, MotionNotify, ev))
        ;   /* discard all queued motion notify events */

#if 0
      fprintf(stderr,"{ %d, %d } -> { %d, %d }\n",
            XROOT(ev->xmotion),
            YROOT(ev->xmotion),
            SCALEX(XROOT(ev->xmotion)),
            SCALEY(YROOT(ev->xmotion));
#endif
      i=sendpointerevent(XROOT(ev->xmotion),
                     YROOT(ev->xmotion),
                     (ev->xmotion.state & 0x1f00) >> 8);

      if(ev->xmotion.state & 0x1f00) return 1;

          /*
           * Things get complicated for multi-screen X servers,
           * particularly if the PC screen is "inserted" between two
           * X screens.
           * - GRM
           */
        /* First, check for normal edges (applies to single screen or
         * screen edge that doesn't switch screens)
         */
        if (ev->xmotion.same_screen)
      {
          x = XROOT(ev->xmotion);
          y = YROOT(ev->xmotion);
          warpWindow = DefaultRootWindow(dpy);
        switch(edge)
        {
          case EDGE_NORTH: 
              d=y >= displayHeight-edge_width;
              y = edge_width;
              break;
          case EDGE_SOUTH:
              d=y < edge_width;
              y = displayHeight - edge_width -1;
              break;
          case EDGE_EAST:
              d=x < edge_width;
              x = displayWidth -  edge_width -1;
              break;
          case EDGE_WEST:
              d=x >= displayWidth-edge_width;
              x = edge_width;
              break;
        }
        } else {
            /*
             * Different screen. Depending on where the pointer ended up,
             * we warp to either our default screen or the new screen.
             * 
             * If the pointer is "near" the edge we're watching, then the
             * user moved the pointer off the _opposite_ side of the PC
             * screen, and the pointer should reappear on the original screen.
             * 
             * If not, then the pointer was moved off some other edge - and
             * we just release the pointer.
             * 
             * In either case, the pointer's location - relative to the
             * screen - is not moved.
             * 
             * - GRM
             */
          warpWindow = ev->xmotion.root;
          x = XROOT(ev->xmotion);
          y = YROOT(ev->xmotion);
        switch(edge)
        {
          case EDGE_NORTH:
              d=YROOT(ev->xmotion) < displayHeight / 2;
              break;
          case EDGE_SOUTH:
              d=YROOT(ev->xmotion) > displayHeight / 2;
              break;
          case EDGE_EAST:
              d=XROOT(ev->xmotion) > displayWidth / 2;
              break;
          case EDGE_WEST:
              d=XROOT(ev->xmotion) < displayWidth / 2;
              break;
        }
          if (d) {
            warpWindow = DefaultRootWindow(dpy);
          }
          d = 1;
              
        }
          
      if(d)
      {
        ungrabit(x, y, warpWindow);
        return 1;
      }
      }
      return 1;
      
    case ButtonPress:
    case ButtonRelease:
      if (emulate_wheel &&
        ev->xbutton.button >= 4 &&
        ev->xbutton.button <= 5)
      {
      int l;
      if (ev->xbutton.button == wheel_button_up)
        ks = XK_Up;
      else
        ks = XK_Down;

      if(ev->type == ButtonPress)
      {
        for(l=1;l<scroll_lines;l++)
        {
          SendKeyEvent(ks, 1); /* keydown */
          SendKeyEvent(ks, 0); /* keyup */
        }
      }

      SendKeyEvent(ks, ev->type == ButtonPress); /* keydown */
      break;
      }

      if (emulate_nav &&
        ev->xbutton.button >= 5 &&
        ev->xbutton.button <= 7)
      {
      int l, ctrlcode;
      if (ev->xbutton.button == 6)
        ks = XK_Left;
      else
        ks = XK_Right;

      ctrlcode=XKeysymToKeycode(dpy, XK_Alt_L);
      SendKeyEvent(XK_Alt_L, ev->type == ButtonPress);
      modifierPressed[ctrlcode]=ev->type == ButtonPress;
      SendKeyEvent(ks, ev->type == ButtonPress); /* keydown */
      break;
      }

      if(mac_mode && ev->xbutton.button == 3)
      {
      int ctrlcode=XKeysymToKeycode(dpy, XK_Control_L);
      SendKeyEvent(XK_Control_L, ev->type == ButtonPress);
      modifierPressed[ctrlcode]=ev->type == ButtonPress;
      }

      if (ev->type == ButtonPress) {
      buttonMask = (((ev->xbutton.state & 0x1f00) >> 8) |
                  (1 << (ev->xbutton.button - 1)));
      } else {
      buttonMask = (((ev->xbutton.state & 0x1f00) >> 8) &
                  ~(1 << (ev->xbutton.button - 1)));
      }
      
      return sendpointerevent(XROOT(ev->xbutton),
                        YROOT(ev->xbutton),
                        buttonMask);
      
    case KeyPress:
    case KeyRelease:
    {
      char keyname[256];
      keyname[0] = '\0';


      XLookupString(&ev->xkey, keyname, 256, &ks, NULL);
/*      fprintf(stderr,"Pressing %x (%c) name=%s  code=%d\n",ks,ks,keyname,ev->xkey.keycode); */

      if(ev->type == KeyPress && 
       ev->xkey.keycode == grabkey &&
       (ev->xkey.state == grabmod ||
        (ev->xkey.state & ~ Mod2Mask ) == grabmod ))
      {
      saved_remote_xpos=XROOT(ev->xkey);
      saved_remote_ypos=YROOT(ev->xkey);
      ungrabit(saved_xpos,saved_ypos,DefaultRootWindow(dpy));
      return 1;
      }
      if (IsModifierKey(ks)) {
      ks = XKeycodeToKeysym(dpy, ev->xkey.keycode, 0);

      /* Ignore AltGr key, it is handled by XLookupString */
      if( ks == XK_Mode_switch ) return True;

      modifierPressed[ev->xkey.keycode] = (ev->type == KeyPress);
      } else {
      /* This fixes the 'shift-tab' problem - Hubbe */
      switch(ks)
      {
#if XK_ISO_Left_Tab != 0x1000ff74
        case 0x1000ff74: /* HP-UX */
#endif
        case XK_ISO_Left_Tab: ks=XK_Tab; break;
      }
      }

      if(debug)
      fprintf(stderr,"  --> %x (%c) name=%s (%s)\n",ks,ks,keyname,
            ev->type == KeyPress ? "down" : "up");

      /* We assume that typing means an unlocked display */
      remote_is_locked=0;

      return SendKeyEvent(ks, (ev->type == KeyPress));
    }
      
    case ClientMessage:
      if ((ev->xclient.message_type == wmProtocols) &&
        (ev->xclient.data.l[0] == wmDeleteWindow))
      {
          ShutdownX();
          exit(0);
      }
      break;
    }

    return True;
}

int get_root_int_prop(Atom property)
{
  unsigned char *data=0;
  Atom t;
  int format;
  unsigned long len;
  unsigned long bytes_left;
  int ret=XGetWindowProperty(dpy,
                       DefaultRootWindow(dpy),
                       property,
                       0,
                       1,
                       0, /* delete */
                       XA_CARDINAL,
                       &t,
                       &format,
                       &len,
                       &bytes_left,
                       &data);

  ret = *(CARD32 *)data;
  
  if(data)
    XFree(data);

  return ret;
}

void check_desktop(void)
{
  int t=requested_desktop;

  current_desktop=get_root_int_prop(current_desktop_atom);
  
  switch(t)
  {
    case -2: t=current_desktop; break;
    case -1:
      t=get_root_int_prop(number_of_desktops_atom)-1;
      if(t<0) t=0;
      break;
  }
  if( t == current_desktop)
  {
    mapwindow();
  }else{
    hidewindow();
  }
}

/*
 * HandleRootEvent.
 */

static Bool HandleRootEvent(XEvent *ev)
{
  char *str;
  int len;
  
  Bool nowOnScreen;
  Bool grab;
  
  int x, y;
  
  switch (ev->type)
  {
    case KeyPress:
    {
      KeySym ks;
      char keyname[256];
      keyname[0] = '\0';
      XLookupString(&ev->xkey, keyname, 256, &ks, NULL);
#ifdef DEBUG
      fprintf(stderr,"ROOT: Pressing %x (%c) name=%s  code=%d\n",ks,ks,keyname,ev->xkey.keycode);
#endif
      if(ev->xkey.keycode)
      {
      saved_xpos=XROOT(ev->xkey);
      saved_ypos=YROOT(ev->xkey);
      grabit(saved_remote_xpos, saved_remote_ypos, 0);
      }
      break;
    }

    case EnterNotify:
    case LeaveNotify:
      /*
       * Ignore the event if it's due to leaving our window. This will
       * be after an ungrab.
       */
      if (ev->xcrossing.subwindow == topLevel &&
          !ev->xcrossing.same_screen) {
      break;
      }
      
      grab = 0;
      if(!grabbed)
      {
      nowOnScreen =  ev->xcrossing.same_screen;
      if (mouseOnScreen != nowOnScreen) {
        /*
         * Mouse has left, or entered, the screen. We must grab if
         * the mouse is now near the edge we're watching.
         * 
         * If we do grab, the mouse coordinates are left alone.
         * The test, however, depends on whether the mouse entered
         * or left the screen.
         * 
         * - GRM
         */
        x = XROOT(ev->xcrossing);
        y = XROOT(ev->xcrossing);
        if (!nowOnScreen) {
          x = enter_translate(EW,displayWidth,XROOT(ev->xcrossing));
          y = enter_translate(NS,displayHeight,YROOT(ev->xcrossing));
        }
        switch(edge)
        {
          case EDGE_NORTH:
            grab=y < displayHeight / 2;
            break;
          case EDGE_SOUTH:
            grab=y > displayHeight / 2;
            break;
          case EDGE_EAST:
            grab=x > displayWidth / 2;
            break;
          case EDGE_WEST:
            grab=x < displayWidth / 2;
            break;
        }
      }
      mouseOnScreen = nowOnScreen;
      }
      
      /*
       * Do not grab if this is the result of an ungrab
       * or grab (caused by us, usually).
       * 
       * - GRM
       */
      if(grab && ev->xcrossing.mode == NotifyNormal)
      {
      grabit(enter_translate(EW,displayWidth ,XROOT(ev->xcrossing)),
             enter_translate(NS,displayHeight,YROOT(ev->xcrossing)),
             ev->xcrossing.state);
      }
      break;
      
    case PropertyNotify:
      if (ev->xproperty.atom == XA_CUT_BUFFER0)
      {
      str = XFetchBytes(dpy, &len);
      if (str) {
#ifdef DEBUG
        fprintf(stderr,"GOT CUT TEXT: \"%s\"\n",str);
#endif
        if (!SendClientCutText(str, len))
          return False;
        XFree(str);
      }
      }
      if(ev->xproperty.atom == current_desktop_atom ||
       ev->xproperty.atom == number_of_desktops_atom)
      check_desktop();

      break;
  }
  
  return True;
}

void handle_cut_text(char *str, size_t len)
{
  XWindowAttributes   attrs;
  
  if(client_selection_text)
    free((char *)client_selection_text);
  
  client_selection_text_length=len;
  client_selection_text = str;
  
  XGetWindowAttributes(dpy, RootWindow(dpy, 0), &attrs);
  XSelectInput(dpy, RootWindow(dpy, 0),
             attrs.your_event_mask & ~PropertyChangeMask);
  XStoreBytes(dpy, str, len);
  XSetSelectionOwner(dpy, XA_PRIMARY, topLevel, CurrentTime);
  XSelectInput(dpy, RootWindow(dpy, 0), attrs.your_event_mask);
}



/*
 * AllXEventsPredicate is needed to make XCheckIfEvent return all events.
 */

Bool
AllXEventsPredicate(Display *dpy, XEvent *ev, char *arg)
{
  return True;
}


void sethotkey(char *key)
{
  grabmod = 0;

#define GOBBLE(X,Y)                                   \
    if(!strncasecmp(key,X "-",sizeof(X) +1 -sizeof(""))) {  \
      grabmod|=Y;                               \
      key+=sizeof(X) +1 -sizeof("");                        \
      continue;                                       \
    }

  while(1)
  {
    GOBBLE("s",ShiftMask);
    GOBBLE("shift",ShiftMask);

    GOBBLE("c",ControlMask);
    GOBBLE("ctrl",ControlMask);
    GOBBLE("control",ControlMask);

    GOBBLE("a",Mod1Mask);
    GOBBLE("alt",Mod1Mask);
    GOBBLE("mod1",Mod1Mask);

    GOBBLE("mod2",Mod2Mask);

    GOBBLE("m",Mod3Mask);
    GOBBLE("meta",Mod3Mask);
    GOBBLE("mod3",Mod3Mask);

    GOBBLE("super",Mod4Mask);
    GOBBLE("mod4",Mod4Mask);

    GOBBLE("hyper",Mod5Mask);
    GOBBLE("mod5",Mod5Mask);

    break;
  }

  grabkeysym=XStringToKeysym(key);
}

Generated by  Doxygen 1.6.0   Back to index