1 /* vi:set ts=8 sts=4 sw=4:
3 * VIM - Vi IMproved by Bram Moolenaar
4 * X-Windows communication by Flemming Madsen
6 * Do ":help uganda" in Vim to read copying and usage conditions.
7 * Do ":help credits" in Vim to see a list of people who contributed.
8 * See README.txt for an overview of the Vim source code.
10 * Client for sending commands to an '+xcmdsrv' enabled vim.
11 * This is mostly a de-Vimified version of if_xcmdsrv.c in vim.
12 * See that file for a protocol specification.
14 * You can make a test program with a Makefile like:
15 * xcmdsrv_client: xcmdsrv_client.c
16 * cc -o $@ -g -DMAIN -I/usr/X11R6/include -L/usr/X11R6/lib $< -lX11
24 #include <sys/types.h>
29 #include <X11/Intrinsic.h>
30 #include <X11/Xatom.h>
35 char * sendToVim
__ARGS((Display
*dpy
, char *name
, char *cmd
, int asKeys
, int *code
));
38 /* A sample program */
39 main(int argc
, char **argv
)
46 if ((res
= sendToVim(XOpenDisplay(NULL
), argv
[2], argv
[3],
47 argv
[1][0] != 'e', &code
)) != NULL
)
50 printf("Error code returned: %d\n", code
);
56 fprintf(stderr
, "Usage: %s {k|e} <server> <command>", argv
[0]);
63 * Maximum size property that can be read at one time by
67 #define MAX_PROP_WORDS 100000
70 * Forward declarations for procedures defined later in this file:
73 static int x_error_check
__ARGS((Display
*dpy
, XErrorEvent
*error_event
));
74 static int AppendPropCarefully
__ARGS((Display
*display
,
75 Window window
, Atom property
, char *value
, int length
));
76 static Window LookupName
__ARGS((Display
*dpy
, char *name
,
77 int delete, char **loose
));
78 static int SendInit
__ARGS((Display
*dpy
));
79 static char *SendEventProc
__ARGS((Display
*dpy
, XEvent
*eventPtr
,
80 int expect
, int *code
));
81 static int IsSerialName
__ARGS((char *name
));
83 /* Private variables */
84 static Atom registryProperty
= None
;
85 static Atom commProperty
= None
;
86 static Window commWindow
= None
;
87 static int got_x_error
= FALSE
;
92 * Send to an instance of Vim via the X display.
95 * A string with the result or NULL. Caller must free if non-NULL
99 sendToVim(dpy
, name
, cmd
, asKeys
, code
)
100 Display
*dpy
; /* Where to send. */
101 char *name
; /* Where to send. */
102 char *cmd
; /* What to send. */
103 int asKeys
; /* Interpret as keystrokes or expr ? */
104 int *code
; /* Return code. 0 => OK */
108 XErrorHandler old_handler
;
109 #define STATIC_SPACE 500
110 char *property
, staticSpace
[STATIC_SPACE
];
113 static int serial
= 0; /* Running count of sent commands.
114 * Used to give each command a
115 * different serial number. */
117 XPropertyEvent
*e
= (XPropertyEvent
*)&event
;
120 char *loosename
= NULL
;
122 if (commProperty
== None
&& dpy
!= NULL
)
124 if (SendInit(dpy
) < 0)
129 * Bind the server name to a communication window.
131 * Find any survivor with a serialno attached to the name if the
132 * original registrant of the wanted name is no longer present.
134 * Delete any lingering names from dead editors.
137 old_handler
= XSetErrorHandler(x_error_check
);
141 w
= LookupName(dpy
, name
, 0, &loosename
);
142 /* Check that the window is hot */
145 plist
= XListProperties(dpy
, w
, &res
);
151 LookupName(dpy
, loosename
? loosename
: name
,
152 /*DELETE=*/TRUE
, NULL
);
160 fprintf(stderr
, "no registered server named %s\n", name
);
163 else if (loosename
!= NULL
)
167 * Send the command to target interpreter by appending it to the
168 * comm window in the communication window.
171 length
= strlen(name
) + strlen(cmd
) + 10;
172 if (length
<= STATIC_SPACE
)
173 property
= staticSpace
;
175 property
= (char *) malloc((unsigned) length
);
178 sprintf(property
, "%c%c%c-n %s%c-s %s",
179 0, asKeys
? 'k' : 'c', 0, name
, 0, cmd
);
180 if (name
== loosename
)
184 /* Add a back reference to our comm window */
185 sprintf(property
+ length
, "%c-r %x %d", 0, (uint
) commWindow
, serial
);
186 length
+= strlen(property
+ length
+ 1) + 1;
189 res
= AppendPropCarefully(dpy
, w
, commProperty
, property
, length
+ 1);
190 if (length
> STATIC_SPACE
)
194 fprintf(stderr
, "Failed to send command to the destination program\n");
198 if (asKeys
) /* There is no answer for this - Keys are sent async */
203 * Enter a loop processing X events & pooling chars until we see the result
206 #define SEND_MSEC_POLL 50
209 while ((time((time_t *) 0) - start
) < 60)
211 /* Look out for the answer */
215 fds
.fd
= ConnectionNumber(dpy
);
217 if (poll(&fds
, 1, SEND_MSEC_POLL
) < 0)
224 tv
.tv_usec
= SEND_MSEC_POLL
* 1000;
226 FD_SET(ConnectionNumber(dpy
), &fds
);
227 if (select(ConnectionNumber(dpy
) + 1, &fds
, NULL
, NULL
, &tv
) < 0)
230 while (XEventsQueued(dpy
, QueuedAfterReading
) > 0)
232 XNextEvent(dpy
, &event
);
233 if (event
.type
== PropertyNotify
&& e
->window
== commWindow
)
234 if ((result
= SendEventProc(dpy
, &event
, serial
, code
)) != NULL
)
244 * This procedure is called to initialize the
245 * communication channels for sending commands and
253 XErrorHandler old_handler
;
256 * Create the window used for communication, and set up an
257 * event handler for it.
259 old_handler
= XSetErrorHandler(x_error_check
);
262 commProperty
= XInternAtom(dpy
, "Comm", False
);
263 /* Change this back to "InterpRegistry" to talk to tk processes */
264 registryProperty
= XInternAtom(dpy
, "VimRegistry", False
);
266 if (commWindow
== None
)
269 XCreateSimpleWindow(dpy
, XDefaultRootWindow(dpy
),
270 getpid(), 0, 10, 10, 0,
271 WhitePixel(dpy
, DefaultScreen(dpy
)),
272 WhitePixel(dpy
, DefaultScreen(dpy
)));
273 XSelectInput(dpy
, commWindow
, PropertyChangeMask
);
277 (void) XSetErrorHandler(old_handler
);
279 return got_x_error
? -1 : 0;
284 * Given an interpreter name, see if the name exists in
285 * the interpreter registry for a particular display.
288 * If the given name is registered, return the ID of
289 * the window associated with the name. If the name
290 * isn't registered, then return 0.
294 LookupName(dpy
, name
, delete, loose
)
295 Display
*dpy
; /* Display whose registry to check. */
296 char *name
; /* Name of an interpreter. */
297 int delete; /* If non-zero, delete info about name. */
298 char **loose
; /* Do another search matching -999 if not found
299 Return result here if a match is found */
301 unsigned char *regProp
, *entry
;
303 int result
, actualFormat
;
304 unsigned long numItems
, bytesAfter
;
309 * Read the registry property.
313 result
= XGetWindowProperty(dpy
, RootWindow(dpy
, 0), registryProperty
, 0,
314 MAX_PROP_WORDS
, False
, XA_STRING
, &actualType
,
315 &actualFormat
, &numItems
, &bytesAfter
,
318 if (actualType
== None
)
322 * If the property is improperly formed, then delete it.
325 if ((result
!= Success
) || (actualFormat
!= 8) || (actualType
!= XA_STRING
))
329 XDeleteProperty(dpy
, RootWindow(dpy
, 0), registryProperty
);
334 * Scan the property for the desired name.
338 entry
= NULL
; /* Not needed, but eliminates compiler warning. */
339 for (p
= regProp
; (p
- regProp
) < numItems
; )
342 while ((*p
!= 0) && (!isspace(*p
)))
344 if ((*p
!= 0) && (strcasecmp(name
, p
+ 1) == 0))
346 sscanf(entry
, "%x", (uint
*) &returnValue
);
354 if (loose
!= NULL
&& returnValue
== None
&& !IsSerialName(name
))
356 for (p
= regProp
; (p
- regProp
) < numItems
; )
359 while ((*p
!= 0) && (!isspace(*p
)))
361 if ((*p
!= 0) && IsSerialName(p
+ 1)
362 && (strncmp(name
, p
+ 1, strlen(name
)) == 0))
364 sscanf(entry
, "%x", (uint
*) &returnValue
);
365 *loose
= strdup(p
+ 1);
375 * Delete the property, if that is desired (copy down the
376 * remainder of the registry property to overlay the deleted
377 * info, then rewrite the property).
380 if ((delete) && (returnValue
!= None
))
387 count
= numItems
- (p
-regProp
);
389 memcpy(entry
, p
, count
);
390 XChangeProperty(dpy
, RootWindow(dpy
, 0), registryProperty
, XA_STRING
,
391 8, PropModeReplace
, regProp
,
392 (int) (numItems
- (p
-entry
)));
401 SendEventProc(dpy
, eventPtr
, expected
, code
)
403 XEvent
*eventPtr
; /* Information about event. */
404 int expected
; /* The one were waiting for */
405 int *code
; /* Return code. 0 => OK */
407 unsigned char *propInfo
;
409 int result
, actualFormat
;
411 unsigned long numItems
, bytesAfter
;
414 if ((eventPtr
->xproperty
.atom
!= commProperty
)
415 || (eventPtr
->xproperty
.state
!= PropertyNewValue
))
421 * Read the comm property and delete it.
425 result
= XGetWindowProperty(dpy
, commWindow
, commProperty
, 0,
426 MAX_PROP_WORDS
, True
, XA_STRING
, &actualType
,
427 &actualFormat
, &numItems
, &bytesAfter
,
431 * If the property doesn't exist or is improperly formed
435 if ((result
!= Success
) || (actualType
!= XA_STRING
)
436 || (actualFormat
!= 8))
438 if (propInfo
!= NULL
)
446 * Several commands and results could arrive in the property at
447 * one time; each iteration through the outer loop handles a
448 * single command or result.
451 for (p
= propInfo
; (p
- propInfo
) < numItems
; )
454 * Ignore leading NULs; each command or result starts with a
455 * NUL so that no matter how badly formed a preceding command
456 * is, we'll be able to tell that a new command/result is
466 if ((*p
== 'r') && (p
[1] == 0))
468 int serial
, gotSerial
;
472 * This is a reply to some command that we sent out. Iterate
473 * over all of its options. Stop when we reach the end of the
474 * property or something that doesn't look like an option.
481 while (((p
-propInfo
) < numItems
) && (*p
== '-'))
490 if (sscanf(p
+ 2, " %d", &serial
) == 1)
494 if (sscanf(p
+ 2, " %d", &retCode
) != 1)
508 return serial
== expected
? strdup(res
) : NULL
;
513 * Didn't recognize this thing. Just skip through the next
514 * null character and try again.
515 * Also, throw away commands that we cant process anyway.
527 * AppendPropCarefully --
529 * Append a given property to a given window, but set up
530 * an X error handler so that if the append fails this
531 * procedure can return an error code rather than having
535 * 0 on OK - -1 on error
536 *--------------------------------------------------------------
540 AppendPropCarefully(dpy
, window
, property
, value
, length
)
541 Display
*dpy
; /* Display on which to operate. */
542 Window window
; /* Window whose property is to
544 Atom property
; /* Name of property. */
545 char *value
; /* Characters to append to property. */
546 int length
; /* How much to append */
548 XErrorHandler old_handler
;
550 old_handler
= XSetErrorHandler(x_error_check
);
552 XChangeProperty(dpy
, window
, property
, XA_STRING
, 8,
553 PropModeAppend
, value
, length
);
555 (void) XSetErrorHandler(old_handler
);
556 return got_x_error
? -1 : 0;
561 * Another X Error handler, just used to check for errors.
565 x_error_check(dpy
, error_event
)
567 XErrorEvent
*error_event
;
574 * Check if "str" looks like it had a serial number appended.
575 * Actually just checks if the name ends in a digit.
581 int len
= strlen(str
);
583 return (len
> 1 && isdigit(str
[len
- 1]));