Logo Search packages:      
Sourcecode: kdeaddons version File versions

xvim.cpp

/* Copyright (C) 2002 Mickael Marchand <marchand@kde.org>

       This program 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 program 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 program; see the file COPYING.  If not, write to
       the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
       Boston, MA 02111-1307, USA.
       */
#include <stdlib.h>
#include <time.h>
#include <sys/types.h>
#include <unistd.h>
#include <ctype.h>

#include "xvim.h"
static int  got_x_error;

/*
 * Another X Error handler, just used to check for errors.
 */
/* ARGSUSED */
static int x_error_check(Display *, XErrorEvent *)
{
      got_x_error = TRUE;
      return 0;
}

XVim::XVim() {
      registryProperty = None;
      commProperty = None;
      commWindow = None;
      got_x_error = FALSE;
}

XVim::~XVim() {

}

//    Display     *dpy;             /* Where to send. */
//    char  *name;                  /* Where to send. */
//    char  *cmd;             /* What to send. */
//    int         asKeys;                 /* Interpret as keystrokes or expr ? */
//    int         *code;                  /* Return code. 0 => OK */
char * XVim::sendToVim(Display *dpy, const char *name, const char *cmd, int asKeys,int *code)
{
      Window          w;
      Atom      *plist;
      XErrorHandler   old_handler;
      char      *property, staticSpace[STATIC_SPACE];
      int             length;
      int             res;
      static int      serial = 0;   /* Running count of sent commands.
                                                                                           * Used to give each command a
                                                                                           * different serial number. */
      XEvent          event;
      XPropertyEvent  *e = (XPropertyEvent *)&event;
      time_t          start;
      char      *result;
      char      *loosename = NULL;

      if (commProperty == None && dpy != NULL)
      {
            if (SendInit(dpy) < 0) {
                  *code = -1;
                  return NULL;
            }
      }

      /*
       * Bind the server name to a communication window.
       *
       * Find any survivor with a serialno attached to the name if the
       * original registrant of the wanted name is no longer present.
       *
       * Delete any lingering names from dead editors.
       */

      old_handler = XSetErrorHandler(x_error_check);
      while (TRUE)
      {
            got_x_error = FALSE;
            w = LookupName(dpy, name, 0, &loosename);
            /* Check that the window is hot */
            if (w != None)
            {
                  plist = XListProperties(dpy, w, &res);
                  XSync(dpy, False);
                  if (plist != NULL)
                        XFree(plist);
                  if (got_x_error)
                  {
                        LookupName(dpy, loosename ? loosename : name, /*DELETE=*/TRUE, NULL);
                        continue;
                  }
            }
            break;
      }
      if (w == None)
      {
            fprintf(stderr, "no registered server named %s\n", name);
            *code=-1;
            return NULL;
      }
      else if (loosename != NULL)
            name = loosename;

      /*
       * Send the command to target interpreter by appending it to the
       * comm window in the communication window.
       */

      length = strlen(name) + strlen(cmd) + 10;
      if (length <= STATIC_SPACE)
            property = staticSpace;
      else
            property = (char *) malloc((unsigned) length);

      serial++;
      sprintf(property, "%c%c%c-n %s%c-s %s", 0, asKeys ? 'k' : 'c', 0, name, 0, cmd);
      if (name == loosename)
            free(loosename);
      if (!asKeys)
      {
            /* Add a back reference to our comm window */
            sprintf(property + length, "%c-r %x %d", 0, (uint) commWindow, serial);
            length += strlen(property + length + 1) + 1;
      }

      res = AppendPropCarefully(dpy, w, commProperty, property, length + 1);
      if (length > STATIC_SPACE)
            free(property);
      if (res < 0)
      {
            fprintf(stderr, "Failed to send command to the destination program\n");
            *code=-1;
            return NULL;
      }

      if (asKeys) /* There is no answer for this - Keys are sent async */
            return NULL;

      /*
       * Enter a loop processing X events & pooling chars until we see the result
       */

      time(&start);
      while ((time((time_t *) 0) - start) < 60)
      {
            /* Look out for the answer */
#ifndef HAVE_SELECT
            struct pollfd   fds;

            fds.fd = ConnectionNumber(dpy);
            fds.events = POLLIN;
            if (poll(&fds, 1, SEND_MSEC_POLL) < 0)
                  break;
#else
            fd_set          fds;
            struct timeval  tv;

            tv.tv_sec = 0;
            tv.tv_usec =  SEND_MSEC_POLL * 1000;
            FD_ZERO(&fds);
            FD_SET(ConnectionNumber(dpy), &fds);
            if (select(ConnectionNumber(dpy) + 1, &fds, NULL, NULL, &tv) < 0)
                  break;
#endif
            while (XEventsQueued(dpy, QueuedAfterReading) > 0)
            {
                  XNextEvent(dpy, &event);
                  if (event.type == PropertyNotify && e->window == commWindow)
                        if ((result = SendEventProc(dpy, &event, serial, code)) != NULL)
                              return result;
            }
      }
      *code=-1;
      return NULL;
}

/*
 * SendInit --
 *    This procedure is called to initialize the
 *    communication channels for sending commands and
 *    receiving results.
 */

int XVim::SendInit(Display *dpy) {
      XErrorHandler old_handler;

      /*
       * Create the window used for communication, and set up an
       * event handler for it.
       */
      old_handler = XSetErrorHandler(x_error_check);
      got_x_error = FALSE;

      commProperty = XInternAtom(dpy, "Comm", False);
      /* Change this back to "InterpRegistry" to talk to tk processes */
      registryProperty = XInternAtom(dpy, "VimRegistry", False);

      if (commWindow == None)
      {
            commWindow = XCreateSimpleWindow(dpy, XDefaultRootWindow(dpy),
                        getpid(), 0, 10, 10, 0,
                        WhitePixel(dpy, DefaultScreen(dpy)),
                        WhitePixel(dpy, DefaultScreen(dpy)));
            XSelectInput(dpy, commWindow, PropertyChangeMask);
      }

      XSync(dpy, False);
      (void) XSetErrorHandler(old_handler);

      return got_x_error ? -1 : 0;
}


/*
 * LookupName --
 *    Given an interpreter name, see if the name exists in
 *    the interpreter registry for a particular display.
 *
 * Results:
 *    If the given name is registered, return the ID of
 *    the window associated with the name.  If the name
 *    isn't registered, then return 0.
 */
//    Display *dpy;     /* Display whose registry to check. */
//    char *name;       /* Name of an interpreter. */
//    int delete;       /* If non-zero, delete info about name. */
//    char **loose;     /* Do another search matching -999 if not found
/*                   Return result here if a match is found */
Window XVim::LookupName(Display *dpy,const char* name, int del, char** loose)
{
      unsigned char   *regProp, *entry;
      unsigned char   *p;
      int             result, actualFormat;
      unsigned long   numItems, bytesAfter;
      Atom      actualType;
      Window          returnValue;

      /*
       * Read the registry property.
       */

      regProp = NULL;
      result = XGetWindowProperty(dpy, RootWindow(dpy, 0), registryProperty, 0,
                  MAX_PROP_WORDS, False, XA_STRING, &actualType, &actualFormat, &numItems, &bytesAfter,
                  &regProp);

      if (actualType == None)
            return 0;

      /*
       * If the property is improperly formed, then delete it.
       */

      if ((result != Success) || (actualFormat != 8) || (actualType != XA_STRING))
      {
            if (regProp != NULL)
                  XFree(regProp);
            XDeleteProperty(dpy, RootWindow(dpy, 0), registryProperty);
            return 0;
      }

      /*
       * Scan the property for the desired name.
       */

      returnValue = None;
      entry = NULL;     /* Not needed, but eliminates compiler warning. */
      for (p = regProp; (unsigned)(p - regProp) < numItems; )
      {
            entry = p;
            while ((*p != 0) && (!isspace(*p)))
                  p++;
            if ((*p != 0) && (strcasecmp(name, (char *)(p + 1)) == 0))
            {
                  sscanf((const char*)entry, "%x", (uint*) &returnValue);
                  break;
            }
            while (*p != 0)
                  p++;
            p++;
      }

      if (loose != NULL && returnValue == None && !IsSerialName(name))
      {
            for (p = regProp; (unsigned)(p - regProp) < numItems; )
            {
                  entry = p;
                  while ((*p != 0) && (!isspace(*p)))
                        p++;
                  if ((*p != 0) && IsSerialName((char*)(p + 1))
                              && (strncmp(name, (char*)(p + 1), strlen(name)) == 0))
                  {
                        sscanf((const char*)entry, "%x", (uint*) &returnValue);
                        *loose = strdup((char*)(p + 1));
                        break;
                  }
                  while (*p != 0)
                        p++;
                  p++;
            }
      }

      /*
       * Delete the property, if that is desired (copy down the
       * remainder of the registry property to overlay the deleted
       * info, then rewrite the property).
       */

      if ((del) && (returnValue != None))
      {
            int count;

            while (*p != 0)
                  p++;
            p++;
            count = numItems - (p-regProp);
            if (count > 0)
                  memcpy(entry, p, count);
            XChangeProperty(dpy, RootWindow(dpy, 0), registryProperty, XA_STRING,
                        8, PropModeReplace, regProp, (int) (numItems - (p-entry)));
            XSync(dpy, False);
      }

      XFree(regProp);
      return returnValue;
}

//    Display        *dpy;
//    XEvent          *eventPtr;          /* Information about event. */
//    int             expected;           /* The one were waiting for */
//    int             *code;              /* Return code. 0 => OK */
char * XVim::SendEventProc(Display* dpy, XEvent *eventPtr, int expected,int* code)
{
      unsigned char   *propInfo;
      unsigned char   *p;
      int             result, actualFormat;
      int             retCode;
      unsigned long   numItems, bytesAfter;
      Atom      actualType;

      if ((eventPtr->xproperty.atom != commProperty) || (eventPtr->xproperty.state != PropertyNewValue))
            return NULL;

      /*
       * Read the comm property and delete it.
       */

      propInfo = NULL;
      result = XGetWindowProperty(dpy, commWindow, commProperty, 0,
                  MAX_PROP_WORDS, True, XA_STRING, &actualType, &actualFormat, &numItems, &bytesAfter,
                  &propInfo);

      /*
       * If the property doesn't exist or is improperly formed
       * then ignore it.
       */

      if ((result != Success) || (actualType != XA_STRING) || (actualFormat != 8))
      {
            if (propInfo != NULL)
                  XFree(propInfo);
            return NULL;
      }

      /*
       * Several commands and results could arrive in the property at
       * one time;  each iteration through the outer loop handles a
       * single command or result.
       */

      for (p = propInfo; (unsigned)(p - propInfo) < numItems; )
      {
            /*
             * Ignore leading NULs; each command or result starts with a
             * NUL so that no matter how badly formed a preceding command
             * is, we'll be able to tell that a new command/result is
             * starting.
             */

            if (*p == 0)
            {
                  p++;
                  continue;
            }

            if ((*p == 'r') && (p[1] == 0))
            {
                  int       serial, gotSerial;
                  unsigned char  *res=NULL;

                  /*
                   * This is a reply to some command that we sent out.  Iterate
                   * over all of its options.  Stop when we reach the end of the
                   * property or something that doesn't look like an option.
                   */

                  p += 2;
                  gotSerial = 0;
                  retCode = 0;
                  while (((unsigned)(p-propInfo) < numItems) && (*p == '-'))
                  {
                        switch (p[1])
                        {
                              case 'r':
                                    if (p[2] == ' ')
                                          res = p + 3;
                                    break;
                              case 's':
                                    if (sscanf((const char*)(p + 2), " %d", &serial) == 1)
                                          gotSerial = 1;
                                    break;
                              case 'c':
                                    if (sscanf((const char*)(p + 2), " %d", &retCode) != 1)
                                          retCode = 0;
                                    break;
                        }
                        while (*p != 0)
                              p++;
                        p++;
                  }

                  if (!gotSerial)
                        continue;

                  if (code != NULL)
                        *code = retCode;
                  return serial == expected ? strdup((const char*)res) : NULL;
            }
            else
            {
                  /*
                   * Didn't recognize this thing.  Just skip through the next
                   * null character and try again.
                   * Also, throw away commands that we cant process anyway.
                   */

                  while (*p != 0)
                        p++;
                  p++;
            }
      }
      XFree(propInfo);
      return NULL;
}

/*
 * AppendPropCarefully --
 *
 *    Append a given property to a given window, but set up
 *    an X error handler so that if the append fails this
 *    procedure can return an error code rather than having
 *    Xlib panic.
 *
 *  Return:
 *    0 on OK - -1 on error
 *--------------------------------------------------------------
 */
//    Display *dpy;           /* Display on which to operate. */
//    Window window;          /* Window whose property is to
      /*                 * be modified. */
//    Atom property;          /* Name of property. */
//    char *value;            /* Characters  to append to property. */
//    int  length;            /* How much to append */
int XVim:: AppendPropCarefully(Display *dpy, Window window, Atom property,char * value,int length)
{
      XErrorHandler old_handler;

      old_handler = XSetErrorHandler(x_error_check);
      got_x_error = FALSE;
      XChangeProperty(dpy, window, property, XA_STRING, 8, PropModeAppend, (const unsigned char*)value, length);
      XSync(dpy, False);
      (void) XSetErrorHandler(old_handler);
      return got_x_error ? -1 : 0;
}

/*
 * Check if "str" looks like it had a serial number appended.
 * Actually just checks if the name ends in a digit.
 */
int XVim::IsSerialName(const char *str)
{
      int len = strlen(str);

      return (len > 1 && isdigit(str[len - 1]));
}


Generated by  Doxygen 1.6.0   Back to index