blob: 3f38c55e5b73dbf9e75fc81b113565278552c40e [file] [log] [blame]
Bram Moolenaar071d4272004-06-13 20:20:40 +00001/* vi:set ts=8 sw=8:
2 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 * Visual Workshop integration by Gordon Prieur
5 *
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.
9 */
10
11/*
12 * Integration with Sun Workshop.
13 *
14 * This file should not change much, it's also used by other editors that
15 * connect to Workshop. Consider changing workshop.c instead.
16 */
17/*
18-> consider using MakeSelectionVisible instead of gotoLine hacks
19 to show the line properly
20 -> consider using glue instead of our own message wrapping functions
21 (but can only use glue if we don't have to distribute source)
22*/
23
24#include "vim.h"
25
26#include <stdio.h>
27#include <stdlib.h>
Bram Moolenaar071d4272004-06-13 20:20:40 +000028
29#ifdef INET_SOCKETS
30#include <netdb.h>
31#include <netinet/in.h>
32#else
33#include <sys/un.h>
34#endif
35
Bram Moolenaar071d4272004-06-13 20:20:40 +000036#include <sys/types.h>
37#include <sys/socket.h>
38#include <sys/param.h>
39#ifdef HAVE_LIBGEN_H
40# include <libgen.h>
41#endif
42#include <unistd.h>
43#include <string.h>
44
45#include <X11/Intrinsic.h>
46#include <Xm/Xm.h>
47#include <Xm/AtomMgr.h>
48#include <Xm/PushB.h>
49
50#ifdef HAVE_X11_XPM_H
51# include <X11/xpm.h>
52#else
53# ifdef HAVE_XM_XPMP_H
54# include <Xm/XpmP.h>
55# endif
56#endif
57
58#ifdef HAVE_UTIL_DEBUG_H
59# include <util/debug.h>
60#endif
61#ifdef HAVE_UTIL_MSGI18N_H
62# include <util/msgi18n.h>
63#endif
64
65#include "integration.h" /* <EditPlugin/integration.h> */
66#ifdef HAVE_FRAME_H
67# include <frame.h>
68#endif
69
70#ifndef MAX
71# define MAX(a, b) (a) > (b) ? (a) : (b)
72#endif
73
74#ifndef NOCATGETS
75# define NOCATGETS(x) x
76#endif
77
78/* Functions private to this file */
79static void workshop_connection_closed(void);
Bram Moolenaarba07ce32010-01-06 18:25:34 +010080static void messageFromEserve(XtPointer clientData, int *dum1, XtInputId *dum2);
Bram Moolenaar071d4272004-06-13 20:20:40 +000081static void workshop_disconnect(void);
82static void workshop_sensitivity(int num, char *table);
83static void adjust_sign_name(char *filename);
84static void process_menuItem(char *);
85static void process_toolbarButton(char *);
86static void workshop_set_option_first(char *name, char *value);
87
Bram Moolenaar860cae12010-06-05 23:22:07 +020088static size_t dummy; /* to ignore return value of write() */
Bram Moolenaar071d4272004-06-13 20:20:40 +000089
90#define CMDBUFSIZ 2048
91
92#ifdef DEBUG
93static FILE *dfd;
94static void pldebug(char *, ...);
95static void unrecognised_message(char *);
96
97#define HANDLE_ERRORS(cmd) else unrecognised_message(cmd);
98#else
99#define HANDLE_ERRORS(cmd)
100#endif
101
102/*
103 * Version number of the protocol between an editor and eserve.
104 * This number should be incremented when the protocol
105 * is changed.
106 */
107#define PROTOCOL_VERSION "4.0.0"
108
109static int sd = -1;
110static XtInputId inputHandler; /* Cookie for input */
111
112Boolean save_files = True; /* When true, save all files before build actions */
113
114void
115workshop_connection_closed(void)
116{
117 /*
118 * socket closed on other end
119 */
120 XtRemoveInput(inputHandler);
121 inputHandler = 0;
122 sd = -1;
123}
124
125 static char *
126getCommand(void)
127{
128 int len; /* length of this command */
129 char lenbuf[7]; /* get the length string here */
130 char *newcb; /* used to realloc cmdbuf */
131 static char *cmdbuf;/* get the command string here */
132 static int cbsize;/* size of cmdbuf */
133
134 if ((len = read(sd, &lenbuf, 6)) == 6) {
135 lenbuf[6] = 0; /* Terminate buffer such that atoi() works right */
136 len = atoi(lenbuf);
137 if (cbsize < (len + 1)) {
138 newcb = (char *) realloc(cmdbuf,
139 MAX((len + 256), CMDBUFSIZ));
140 if (newcb != NULL) {
141 cmdbuf = newcb;
142 cbsize = MAX((len + 256), CMDBUFSIZ);
143 }
144 }
145 if (cbsize >= len && (len = read(sd, cmdbuf, len)) > 0) {
146 cmdbuf[len] = 0;
147 return cmdbuf;
148 } else {
149 return NULL;
150 }
151 } else {
152 if (len == 0) { /* EOF */
153 workshop_connection_closed();
154 }
155 return NULL;
156 }
157
158}
159
Bram Moolenaar071d4272004-06-13 20:20:40 +0000160void
Bram Moolenaarba07ce32010-01-06 18:25:34 +0100161messageFromEserve(XtPointer clientData UNUSED,
162 int *dum1 UNUSED,
163 XtInputId *dum2 UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000164{
165 char *cmd; /* the 1st word of the command */
166
167 cmd = getCommand();
168 if (cmd == NULL) {
169 /* We're being shut down by eserve and the "quit" message
170 * didn't arrive before the socket connection got closed */
171 return;
172 }
173#ifdef DEBUG
174 pldebug("%s\n", cmd);
175#endif
176 switch (*cmd) {
177 case 'a':
178 if (cmd[1] == 'c' &&
179 strncmp(cmd, NOCATGETS("ack "), 4) == 0) {
180 int ackNum;
181 char buf[20];
182
183 ackNum = atoi(&cmd[4]);
Bram Moolenaar9c13b352005-05-19 20:53:52 +0000184 vim_snprintf(buf, sizeof(buf),
185 NOCATGETS("ack %d\n"), ackNum);
Bram Moolenaar860cae12010-06-05 23:22:07 +0200186 dummy = write(sd, buf, strlen(buf));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000187 } else if (strncmp(cmd,
188 NOCATGETS("addMarkType "), 12) == 0) {
189 int idx;
190 char *color;
191 char *sign;
192
193 idx = atoi(strtok(&cmd[12], " "));
194 color = strtok(NULL, NOCATGETS("\001"));
195 sign = strtok(NULL, NOCATGETS("\001"));
196 /* Skip space that separates names */
197 if (color) {
198 color++;
199 }
200 if (sign) {
201 sign++;
202 }
Bram Moolenaarba07ce32010-01-06 18:25:34 +0100203 /* Change sign name to accommodate a different size? */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000204 adjust_sign_name(sign);
205 workshop_add_mark_type(idx, color, sign);
206 }
207 HANDLE_ERRORS(cmd);
208 break;
209
210 case 'b':
211 if (strncmp(cmd,
212 NOCATGETS("balloon "), 8) == 0) {
213 char *tip;
214
215 tip = strtok(&cmd[8], NOCATGETS("\001"));
216 workshop_show_balloon_tip(tip);
217 }
218 HANDLE_ERRORS(cmd);
219 break;
220
221 case 'c':
222 if (strncmp(cmd,
223 NOCATGETS("changeMarkType "), 15) == 0) {
224 char *file;
225 int markId;
226 int type;
227
228 file = strtok(&cmd[15], " ");
229 markId = atoi(strtok(NULL, " "));
230 type = atoi(strtok(NULL, " "));
231 workshop_change_mark_type(file, markId, type);
232 }
233 HANDLE_ERRORS(cmd);
234 break;
235
236 case 'd':
237 if (strncmp(cmd, NOCATGETS("deleteMark "), 11) == 0) {
238 char *file;
239 int markId;
240
241 file = strtok(&cmd[11], " ");
242 markId = atoi(strtok(NULL, " "));
243 workshop_delete_mark(file, markId);
244 }
245 HANDLE_ERRORS(cmd);
246 break;
247
248 case 'f':
249 if (cmd[1] == 'o' &&
250 strncmp(cmd, NOCATGETS("footerMsg "), 10) == 0) {
251 int severity;
252 char *message;
253
254 severity =
255 atoi(strtok(&cmd[10], " "));
256 message = strtok(NULL, NOCATGETS("\001"));
257
258 workshop_footer_message(message, severity);
259 } else if (strncmp(cmd,
260 NOCATGETS("frontFile "), 10) == 0) {
261 char *file;
262
263 file = strtok(&cmd[10], " ");
264 workshop_front_file(file);
265 }
266 HANDLE_ERRORS(cmd);
267 break;
268
269 case 'g':
270 if (cmd[1] == 'e' &&
271 strncmp(cmd, NOCATGETS("getMarkLine "), 12) == 0) {
272 char *file;
273 int markid;
274 int line;
275 char buf[100];
276
277 file = strtok(&cmd[12], " ");
278 markid = atoi(strtok(NULL, " "));
279 line = workshop_get_mark_lineno(file, markid);
Bram Moolenaar9c13b352005-05-19 20:53:52 +0000280 vim_snprintf(buf, sizeof(buf),
281 NOCATGETS("markLine %s %d %d\n"),
Bram Moolenaar071d4272004-06-13 20:20:40 +0000282 file, markid, line);
Bram Moolenaar860cae12010-06-05 23:22:07 +0200283 dummy = write(sd, buf, strlen(buf));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000284 } else if (cmd[1] == 'o' && cmd[4] == 'L' &&
285 strncmp(cmd, NOCATGETS("gotoLine "), 9) == 0) {
286 char *file;
287 int lineno;
288
289 file = strtok(&cmd[9], " ");
290 lineno = atoi(strtok(NULL, " "));
291 workshop_goto_line(file, lineno);
292 } else if (strncmp(cmd,
293 NOCATGETS("gotoMark "), 9) == 0) {
294 char *file;
295 int markId;
296 char *message;
297
298 file = strtok(&cmd[9], " ");
299 markId = atoi(strtok(NULL, " "));
300 message = strtok(NULL, NOCATGETS("\001"));
301 workshop_goto_mark(file, markId, message);
302#ifdef NOHANDS_SUPPORT_FUNCTIONS
303 } else if (strcmp(cmd, NOCATGETS("getCurrentFile")) == 0) {
304 char *f = workshop_test_getcurrentfile();
305 char buffer[2*MAXPATHLEN];
Bram Moolenaar9c13b352005-05-19 20:53:52 +0000306 vim_snprintf(buffer, sizeof(buffer),
307 NOCATGETS("currentFile %d %s"),
Bram Moolenaar071d4272004-06-13 20:20:40 +0000308 f ? strlen(f) : 0, f ? f : "");
309 workshop_send_message(buffer);
310 } else if (strcmp(cmd, NOCATGETS("getCursorRow")) == 0) {
311 int row = workshop_test_getcursorrow();
312 char buffer[2*MAXPATHLEN];
Bram Moolenaar9c13b352005-05-19 20:53:52 +0000313 vim_snprintf(buffer, sizeof(buffer),
314 NOCATGETS("cursorRow %d"), row);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000315 workshop_send_message(buffer);
316 } else if (strcmp(cmd, NOCATGETS("getCursorCol")) == 0) {
317 int col = workshop_test_getcursorcol();
318 char buffer[2*MAXPATHLEN];
Bram Moolenaar9c13b352005-05-19 20:53:52 +0000319 vim_snprintf(buffer, sizeof(buffer),
320 NOCATGETS("cursorCol %d"), col);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000321 workshop_send_message(buffer);
322 } else if (strcmp(cmd, NOCATGETS("getCursorRowText")) == 0) {
323 char *t = workshop_test_getcursorrowtext();
324 char buffer[2*MAXPATHLEN];
Bram Moolenaar9c13b352005-05-19 20:53:52 +0000325 vim_snprintf(buffer, sizeof(buffer),
326 NOCATGETS("cursorRowText %d %s"),
Bram Moolenaar071d4272004-06-13 20:20:40 +0000327 t ? strlen(t) : 0, t ? t : "");
328 workshop_send_message(buffer);
329 } else if (strcmp(cmd, NOCATGETS("getSelectedText")) == 0) {
330 char *t = workshop_test_getselectedtext();
331 char buffer[2*MAXPATHLEN];
Bram Moolenaar9c13b352005-05-19 20:53:52 +0000332 vim_snprintf(buffer, sizeof(buffer),
333 NOCATGETS("selectedText %d %s"),
Bram Moolenaar071d4272004-06-13 20:20:40 +0000334 t ? strlen(t) : 0, t ? t : "");
335 workshop_send_message(buffer);
336#endif
337 }
338 HANDLE_ERRORS(cmd);
339 break;
340
341 case 'l':
342 if (strncmp(cmd, NOCATGETS("loadFile "), 9) == 0) {
343 char *file;
344 int line;
345 char *frameid;
346
347 file = strtok(&cmd[9], " ");
348 line = atoi(strtok(NULL, " "));
349 frameid = strtok(NULL, " ");
350 workshop_load_file(file, line, frameid);
351 }
352 HANDLE_ERRORS(cmd);
353 break;
354
355 case 'm': /* Menu, minimize, maximize */
356 if (cmd[1] == 'e' && cmd[4] == 'B' &&
357 strncmp(cmd, NOCATGETS("menuBegin "), 10) == 0) {
358 workshop_menu_begin(&cmd[10]);
359 } else if (cmd[1] == 'e' && cmd[4] == 'I' &&
360 strncmp(cmd, NOCATGETS("menuItem "), 9) == 0) {
361 process_menuItem(cmd);
362 } else if (cmd[1] == 'e' && cmd[4] == 'E' &&
363 strcmp(cmd, NOCATGETS("menuEnd")) == 0) {
364 workshop_menu_end();
365 } else if (cmd[1] == 'a' &&
366 strcmp(cmd, NOCATGETS("maximize")) == 0) {
367 workshop_maximize();
368 } else if (strcmp(cmd, NOCATGETS("minimize")) == 0) {
369 workshop_minimize();
370 }
371 HANDLE_ERRORS(cmd);
372 break;
373
374 case 'o':
375 if (cmd[1] == 'p' &&
376 strcmp(cmd, NOCATGETS("option"))) {
377 char *name;
378 char *value;
379
380 name = strtok(&cmd[7], " ");
381 value = strtok(NULL, " ");
382 workshop_set_option_first(name, value);
383 }
384 HANDLE_ERRORS(cmd);
385 break;
386
387 case 'p':
388 if (strcmp(cmd, NOCATGETS("ping")) == 0) {
389#if 0
390 int pingNum;
391
392 pingNum = atoi(&cmd[5]);
393 workshop_send_ack(ackNum);
Bram Moolenaar09092152010-08-08 16:38:42 +0200394 /* WHAT DO I DO HERE? */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000395#endif
396 }
397 HANDLE_ERRORS(cmd);
398 break;
399
400 case 'q':
401 if (strncmp(cmd, NOCATGETS("quit"), 4) == 0) {
402
403 /* Close the connection. It's important to do
404 * that now, since workshop_quit might be
405 * looking at open files. For example, if you
406 * have modified one of the files without
407 * saving, NEdit will ask you what you want to
408 * do, and spin loop by calling
409 * XtAppProcessEvent while waiting for your
410 * reply. In this case, if we still have an
411 * input handler and the socket has been
412 * closed on the other side when eserve
413 * expired, we will hang in IoWait.
414 */
415 workshop_disconnect();
416
417 workshop_quit();
418 }
419 HANDLE_ERRORS(cmd);
420 break;
421
422 case 'r':
423 if (cmd[1] == 'e' &&
424 strncmp(cmd, NOCATGETS("reloadFile "), 11) == 0) {
425 char *file;
426 int line;
427
428 file = strtok(&cmd[11], " ");
429 line = atoi(strtok(NULL, " "));
430 workshop_reload_file(file, line);
431 }
432 HANDLE_ERRORS(cmd);
433 break;
434
435 case 's':
436 if (cmd[1] == 'e' && cmd[2] == 't' &&
437 strncmp(cmd, NOCATGETS("setMark "), 8) == 0) {
438 char *file;
439 int line;
440 int markId;
441 int type;
442
443 file = strtok(&cmd[8], " ");
444 line = atoi(strtok(NULL, " "));
445 markId = atoi(strtok(NULL, " "));
446 type = atoi(strtok(NULL, " "));
447 workshop_set_mark(file, line, markId, type);
448 } else if (cmd[1] == 'h' &&
449 strncmp(cmd, NOCATGETS("showFile "), 9) == 0) {
450 workshop_show_file(&cmd[9]);
451 } else if (cmd[1] == 'u' &&
452 strncmp(cmd, NOCATGETS("subMenu "), 8) == 0) {
453 char *label;
454
455 label = strtok(&cmd[8], NOCATGETS("\001"));
456 workshop_submenu_begin(label);
457 } else if (cmd[1] == 'u' &&
458 strcmp(cmd, NOCATGETS("subMenuEnd")) == 0) {
459 workshop_submenu_end();
460 } else if (cmd[1] == 'e' && cmd[2] == 'n' &&
461 strncmp(cmd, NOCATGETS("sensitivity "), 12) == 0) {
462 int num;
463 char *bracket;
464 char *table;
465
466 num = atoi(strtok(&cmd[12], " "));
467 bracket = strtok(NULL, " ");
468 if (*bracket != '[') {
469 fprintf(stderr, NOCATGETS("Parsing "
470 "error for sensitivity\n"));
471 } else {
472 table = strtok(NULL, NOCATGETS("]"));
473 workshop_sensitivity(num, table);
474 }
475 } else if (cmd[1] == 'e' && cmd[2] == 'n' && cmd[3] == 'd' &&
476 strncmp(cmd, NOCATGETS("sendVerb "), 9) == 0) {
477 /* Send the given verb back (used for the
478 * debug.lineno callback (such that other tools
479 * can obtain the position coordinates or the
480 * selection) */
481 char *verb;
482
483 verb = strtok(&cmd[9], " ");
484 workshop_perform_verb(verb, NULL);
485 } else if (cmd[1] == 'a' &&
486 strncmp(cmd, NOCATGETS("saveFile "), 9) == 0) {
487 workshop_save_file(&cmd[9]);
488#ifdef NOHANDS_SUPPORT_FUNCTIONS
489 } else if (strncmp(cmd, NOCATGETS("saveSensitivity "), 16) == 0) {
490 char *file;
491
492 file = strtok(&cmd[16], " ");
493 workshop_save_sensitivity(file);
494#endif
495 }
496 HANDLE_ERRORS(cmd);
497 break;
498
499 case 't': /* Toolbar */
500 if (cmd[8] == 'e' &&
501 strncmp(cmd, NOCATGETS("toolbarBegin"), 12) == 0) {
502 workshop_toolbar_begin();
503 } else if (cmd[8] == 'u' &&
504 strncmp(cmd, NOCATGETS("toolbarButton"), 13) == 0) {
505 process_toolbarButton(cmd);
506 } else if (cmd[7] == 'E' &&
507 strcmp(cmd, NOCATGETS("toolbarEnd")) == 0) {
508 workshop_toolbar_end();
509 }
510 HANDLE_ERRORS(cmd);
511 break;
512
513#ifdef DEBUG
514 default:
515 unrecognised_message(cmd);
516 break;
517#endif
518 }
519}
520
521static void
522process_menuItem(
523 char *cmd)
524{
525 char *label = strtok(&cmd[9], NOCATGETS("\001"));
526 char *verb = strtok(NULL, NOCATGETS("\001"));
527 char *acc = strtok(NULL, NOCATGETS("\001"));
528 char *accText = strtok(NULL, NOCATGETS("\001"));
529 char *name = strtok(NULL, NOCATGETS("\001"));
530 char *sense = strtok(NULL, NOCATGETS("\n"));
531 char *filepos = strtok(NULL, NOCATGETS("\n"));
532 if (*acc == '-') {
533 acc = NULL;
534 }
535 if (*accText == '-') {
536 accText = NULL;
537 }
538 workshop_menu_item(label, verb, acc, accText, name, filepos, sense);
539
540}
541
542
543static void
544process_toolbarButton(
545 char *cmd) /* button definition */
546{
547 char *label = strtok(&cmd[14], NOCATGETS("\001"));
548 char *verb = strtok(NULL, NOCATGETS("\001"));
549 char *senseVerb = strtok(NULL, NOCATGETS("\001"));
550 char *filepos = strtok(NULL, NOCATGETS("\001"));
551 char *help = strtok(NULL, NOCATGETS("\001"));
552 char *sense = strtok(NULL, NOCATGETS("\001"));
553 char *file = strtok(NULL, NOCATGETS("\001"));
554 char *left = strtok(NULL, NOCATGETS("\n"));
555
556 if (!strcmp(label, NOCATGETS("-"))) {
557 label = NULL;
558 }
559 if (!strcmp(help, NOCATGETS("-"))) {
560 help = NULL;
561 }
562 if (!strcmp(file, NOCATGETS("-"))) {
563 file = NULL;
564 }
565 if (!strcmp(senseVerb, NOCATGETS("-"))) {
566 senseVerb = NULL;
567 }
568 workshop_toolbar_button(label, verb, senseVerb, filepos, help,
569 sense, file, left);
570}
571
572
573#ifdef DEBUG
574void
575unrecognised_message(
576 char *cmd)
577{
578 pldebug("Unrecognised eserve message:\n\t%s\n", cmd);
579 /* abort(); */
580}
581#endif
582
583
Bram Moolenaarba07ce32010-01-06 18:25:34 +0100584/* Change sign name to accommodate a different size:
Bram Moolenaar071d4272004-06-13 20:20:40 +0000585 * Create the filename based on the height. The filename format
586 * of multisize icons are:
587 * x.xpm : largest icon
588 * x1.xpm : smaller icon
589 * x2.xpm : smallest icon */
590 void
591adjust_sign_name(char *filename)
592{
593 char *s;
594 static int fontSize = -1;
595
596 if (fontSize == -1)
597 fontSize = workshop_get_font_height();
598 if (fontSize == 0)
599 return;
600 if (filename[0] == '-')
601 return;
602
603 /* This is ugly: later we should instead pass the fontheight over
604 * to eserve on startup and let eserve just send the right filenames
605 * to us in the first place
606
607 * I know that the filename will end with 1.xpm (see
608 * GuiEditor.cc`LispPrintSign if you wonder why) */
609 s = filename+strlen(filename)-5;
610 if (fontSize <= 11)
611 strcpy(s, "2.xpm");
612 else if (fontSize <= 15)
613 strcpy(s, "1.xpm");
614 else
615 strcpy(s, ".xpm");
616}
617
Bram Moolenaarba07ce32010-01-06 18:25:34 +0100618#if 0
Bram Moolenaar071d4272004-06-13 20:20:40 +0000619/* Were we invoked by WorkShop? This function can be used early during startup
620 if you want to do things differently if the editor is started standalone
621 or in WorkShop mode. For example, in standalone mode you may not want to
622 add a footer/message area or a sign gutter. */
623int
624workshop_invoked()
625{
626 static int result = -1;
627 if (result == -1) {
628 result = (getenv(NOCATGETS("SPRO_EDITOR_SOCKET")) != NULL);
629 }
630 return result;
631}
Bram Moolenaarba07ce32010-01-06 18:25:34 +0100632#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000633
634/* Connect back to eserve */
635void workshop_connect(XtAppContext context)
636{
637#ifdef INET_SOCKETS
638 struct sockaddr_in server;
639 struct hostent * host;
640 int port;
641#else
642 struct sockaddr_un server;
643#endif
644 char buf[32];
645 char * address;
646#ifdef DEBUG
647 char *file;
648#endif
649
650 address = getenv(NOCATGETS("SPRO_EDITOR_SOCKET"));
651 if (address == NULL) {
652 return;
653 }
654
655#ifdef INET_SOCKETS
656 port = atoi(address);
657
658 if ((sd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
659 PERROR(NOCATGETS("workshop_connect"));
660 return;
661 }
662
663 /* Get the server internet address and put into addr structure */
664 /* fill in the socket address structure and connect to server */
Bram Moolenaar7db5fc82010-05-24 11:59:29 +0200665 vim_memset((char *)&server, '\0', sizeof(server));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000666 server.sin_family = AF_INET;
667 server.sin_port = port;
668 if ((host = gethostbyname(NOCATGETS("localhost"))) == NULL) {
669 PERROR(NOCATGETS("gethostbyname"));
670 sd = -1;
671 return;
672 }
673 memcpy((char *)&server.sin_addr, host->h_addr, host->h_length);
674#else
675 if ((sd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
676 PERROR(NOCATGETS("workshop_connect"));
677 return;
678 }
679
680 server.sun_family = AF_UNIX;
681 strcpy(server.sun_path, address);
682#endif
683 /* Connect to server */
684 if (connect(sd, (struct sockaddr *)&server, sizeof(server))) {
685 if (errno == ECONNREFUSED) {
686 close(sd);
687#ifdef INET_SOCKETS
688 if ((sd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
689 PERROR(NOCATGETS("workshop_connect"));
690 return;
691 }
692#else
693 if ((sd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
694 PERROR(NOCATGETS("workshop_connect"));
695 return;
696 }
697#endif
698 if (connect(sd, (struct sockaddr *)&server,
699 sizeof(server))) {
700 PERROR(NOCATGETS("workshop_connect"));
701 return;
702 }
703
704 } else {
705 PERROR(NOCATGETS("workshop_connect"));
706 return;
707 }
708 }
709
710 /* tell notifier we are interested in being called
711 * when there is input on the editor connection socket
712 */
713 inputHandler = XtAppAddInput(context, sd, (XtPointer) XtInputReadMask,
714 messageFromEserve, NULL);
715#ifdef DEBUG
716 if ((file = getenv(NOCATGETS("SPRO_PLUGIN_DEBUG"))) != NULL) {
717 char buf[BUFSIZ];
718
719 unlink(file);
Bram Moolenaar9c13b352005-05-19 20:53:52 +0000720 vim_snprintf(buf, sizeof(buf), "date > %s", file);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000721 system(buf);
722 dfd = fopen(file, "a");
723 } else {
724 dfd = NULL;
725 }
726#endif
727
Bram Moolenaar9c13b352005-05-19 20:53:52 +0000728 vim_snprintf(buf, sizeof(buf), NOCATGETS("connected %s %s %s\n"),
Bram Moolenaar071d4272004-06-13 20:20:40 +0000729 workshop_get_editor_name(),
730 PROTOCOL_VERSION,
731 workshop_get_editor_version());
Bram Moolenaar860cae12010-06-05 23:22:07 +0200732 dummy = write(sd, buf, strlen(buf));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000733
Bram Moolenaar9c13b352005-05-19 20:53:52 +0000734 vim_snprintf(buf, sizeof(buf), NOCATGETS("ack 1\n"));
Bram Moolenaar860cae12010-06-05 23:22:07 +0200735 dummy = write(sd, buf, strlen(buf));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000736}
737
738void workshop_disconnect()
739{
740 /* Probably need to send some message here */
741
742 /*
743 * socket closed on other end
744 */
745 XtRemoveInput(inputHandler);
746 close(sd);
747 inputHandler = 0;
748 sd = -1;
749
750}
751
752/*
753 * Utility functions
754 */
755
Bram Moolenaar071d4272004-06-13 20:20:40 +0000756
757/* Minimize and maximize shells. From libutil's shell.cc. */
758
759/* utility functions from libutil's shell.cc */
760static Boolean
761isWindowMapped(Display *display, Window win)
762{
763 XWindowAttributes winAttrs;
764 XGetWindowAttributes(display,
765 win,
766 &winAttrs);
767 if (winAttrs.map_state == IsViewable) {
768 return(True);
769 } else {
770 return(False);
771 }
772}
773
774static Boolean
775isMapped(Widget widget)
776{
777 if (widget == NULL) {
778 return(False);
779 }
780
781 if (XtIsRealized(widget) == False) {
782 return(False);
783 }
784
785 return(isWindowMapped(XtDisplay(widget), XtWindow(widget)));
786}
787
788static Boolean
789widgetIsIconified(
790 Widget w)
791{
792 Atom wm_state;
793 Atom act_type; /* actual Atom type returned */
794 int act_fmt; /* actual format returned */
795 u_long nitems_ret; /* number of items returned */
796 u_long bytes_after; /* number of bytes remaining */
797 u_long *property; /* actual property returned */
798
799 /*
800 * If a window is iconified its WM_STATE is set to IconicState. See
801 * ICCCM Version 2.0, section 4.1.3.1 for more details.
802 */
803
804 wm_state = XmInternAtom(XtDisplay(w), NOCATGETS("WM_STATE"), False);
805 if (XtWindow(w) != 0) { /* only check if window exists! */
806 XGetWindowProperty(XtDisplay(w), XtWindow(w), wm_state, 0L, 2L,
807 False, AnyPropertyType, &act_type, &act_fmt, &nitems_ret,
808 &bytes_after, (u_char **) &property);
809 if (nitems_ret == 2 && property[0] == IconicState) {
810 return True;
811 }
812 }
813
814 return False;
815
816} /* end widgetIsIconified */
817
818void
819workshop_minimize_shell(Widget shell)
820{
821 if (shell != NULL &&
822 XtIsObject(shell) &&
823 XtIsRealized(shell) == True) {
824 if (isMapped(shell) == True) {
825 XIconifyWindow(XtDisplay(shell), XtWindow(shell),
826 XScreenNumberOfScreen(XtScreen(shell)));
827 }
828 XtVaSetValues(shell,
829 XmNiconic, True,
830 NULL);
831 }
832}
833
834void workshop_maximize_shell(Widget shell)
835{
836 if (shell != NULL &&
837 XtIsRealized(shell) == True &&
838 widgetIsIconified(shell) == True &&
839 isMapped(shell) == False) {
840 XtMapWidget(shell);
841 /* This used to be
842 XtPopdown(shell);
843 XtPopup(shell, XtGrabNone);
844 However, I found that that would drop any transient
845 windows that had been iconified with the window.
846 According to the ICCCM, XtMapWidget should be used
847 to bring a window from Iconic to Normal state.
848 However, Rich Mauri did a lot of work on this during
849 Bart, and found that XtPopDown,XtPopup was required
850 to fix several bugs involving multiple CDE workspaces.
851 I've tested it now and things seem to work fine but
852 I'm leaving this note for history in case this needs
853 to be revisited.
854 */
855 }
856}
857
858
859Boolean workshop_get_width_height(int *width, int *height)
860{
861 static int wid = 0;
862 static int hgt = 0;
863 static Boolean firstTime = True;
864 static Boolean success = False;
865
866 if (firstTime) {
867 char *settings;
868
869 settings = getenv(NOCATGETS("SPRO_GUI_WIDTH_HEIGHT"));
870 if (settings != NULL) {
871 wid = atoi(settings);
872 settings = strrchr(settings, ':');
873 if (settings++ != NULL) {
874 hgt = atoi(settings);
875 }
876 if (wid > 0 && hgt > 0) {
877 success = True;
878 }
879 firstTime = False;
880 }
881 }
882
883 if (success) {
884 *width = wid;
885 *height = hgt;
886 }
887 return success;
888}
889
Bram Moolenaar071d4272004-06-13 20:20:40 +0000890/*
891 * Toolbar code
892 */
893
894void workshop_sensitivity(int num, char *table)
895{
896 /* build up a verb table */
897 VerbSense *vs;
898 int i;
899 char *s;
900 if ((num < 1) || (num > 500)) {
901 return;
902 }
903
904 vs = (VerbSense *)malloc((num+1)*sizeof(VerbSense));
905
906 /* Point to the individual names (destroys the table string, but
907 * that's okay -- this is more efficient than duplicating strings) */
908 s = table;
909 for (i = 0; i < num; i++) {
910 while (*s == ' ') {
911 s++;
912 }
913 vs[i].verb = s;
914 while (*s && (*s != ' ') && (*s != '\001')) {
915 s++;
916 }
917 if (*s == 0) {
918 vs[i].verb = NULL;
919 break;
920 }
921 if (*s == '\001') {
922 *s = 0;
923 s++;
924 }
925 *s = 0;
926 s++;
927 while (*s == ' ') {
928 s++;
929 }
930 if (*s == '1') {
931 vs[i].sense = 1;
932 } else {
933 vs[i].sense = 0;
934 }
935 s++;
936 }
937 vs[i].verb = NULL;
938
939 workshop_frame_sensitivities(vs);
940
941 free(vs);
942}
943
944/*
945 * Options code
946 */
947/* Set an editor option.
948 * IGNORE an option if you do not recognize it.
949 */
950void workshop_set_option_first(char *name, char *value)
951{
952 /* Currently value can only be on/off. This may change later (for
953 * example to set an option like "balloon evaluate delay", but
954 * for now just convert it into a boolean */
955 Boolean on = !strcmp(value, "on");
956
957 if (!strcmp(name, "workshopkeys")) {
958 workshop_hotkeys(on);
959 } else if (!strcmp(name, "savefiles")) {
960 save_files = on;
961 } else if (!strcmp(name, "balloon")) {
962 workshop_balloon_mode(on);
963 } else if (!strcmp(name, "balloondelay")) {
964 int delay = atoi(value);
965 /* Should I validate the number here?? */
966 workshop_balloon_delay(delay);
967 } else {
968 /* Let editor interpret it */
969 workshop_set_option(name, value);
970 }
971}
972
973
Bram Moolenaar071d4272004-06-13 20:20:40 +0000974void workshop_file_closed_lineno(char *filename, int lineno)
975{
976 char buffer[2*MAXPATHLEN];
Bram Moolenaar9c13b352005-05-19 20:53:52 +0000977 vim_snprintf(buffer, sizeof(buffer),
978 NOCATGETS("deletedFile %s %d\n"), filename, lineno);
Bram Moolenaar860cae12010-06-05 23:22:07 +0200979 dummy = write(sd, buffer, strlen(buffer));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000980}
981
982void workshop_file_opened(char *filename, int readOnly)
983{
984 char buffer[2*MAXPATHLEN];
Bram Moolenaar9c13b352005-05-19 20:53:52 +0000985 vim_snprintf(buffer, sizeof(buffer),
986 NOCATGETS("loadedFile %s %d\n"), filename, readOnly);
Bram Moolenaar860cae12010-06-05 23:22:07 +0200987 dummy = write(sd, buffer, strlen(buffer));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000988}
989
990
991void workshop_file_saved(char *filename)
992{
993 char buffer[2*MAXPATHLEN];
Bram Moolenaar9c13b352005-05-19 20:53:52 +0000994 vim_snprintf(buffer, sizeof(buffer),
995 NOCATGETS("savedFile %s\n"), filename);
Bram Moolenaar860cae12010-06-05 23:22:07 +0200996 dummy = write(sd, buffer, strlen(buffer));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000997
998 /* Let editor report any moved marks that the eserve client
999 * should deal with (for example, moving location-based breakpoints) */
1000 workshop_moved_marks(filename);
1001}
1002
Bram Moolenaar071d4272004-06-13 20:20:40 +00001003void workshop_frame_moved(int new_x, int new_y, int new_w, int new_h)
1004{
1005 char buffer[200];
1006
1007 if (sd >= 0)
1008 {
Bram Moolenaar9c13b352005-05-19 20:53:52 +00001009 vim_snprintf(buffer, sizeof(buffer),
1010 NOCATGETS("frameAt %d %d %d %d\n"),
Bram Moolenaar071d4272004-06-13 20:20:40 +00001011 new_x, new_y, new_w, new_h);
Bram Moolenaar860cae12010-06-05 23:22:07 +02001012 dummy = write(sd, buffer, strlen(buffer));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001013 }
1014}
1015
1016/* A button in the toolbar has been pushed.
1017 * Clientdata is a pointer used by the editor code to figure out the
1018 * positions for this toolbar (probably by storing a window pointer,
1019 * and then fetching the current buffer for that window and looking up
1020 * cursor and selection positions etc.) */
1021void workshop_perform_verb(char *verb, void *clientData)
1022{
1023 char *filename;
1024 int curLine;
1025 int curCol;
1026 int selStartLine;
1027 int selStartCol;
1028 int selEndLine;
1029 int selEndCol;
1030 int selLength;
1031 char *selection;
1032
1033 char buf[2*MAXPATHLEN];
1034/* Later: needsFilePos indicates whether or not we need to fetch all this
1035 * info for this verb... for now, however, it looks as if
1036 * eserve parsing routines depend on it always being present */
1037
1038 if (workshop_get_positions(clientData,
1039 &filename,
1040 &curLine,
1041 &curCol,
1042 &selStartLine,
1043 &selStartCol,
1044 &selEndLine,
1045 &selEndCol,
1046 &selLength,
1047 &selection)) {
1048 if (selection == NULL) {
1049 selection = NOCATGETS("");
1050 }
1051
1052 /* Should I save the files??? This is currently done by checking
1053 if the verb is one of a few recognized ones. Later we can pass
1054 this list from eserve to the editor (it's currently hardcoded in
1055 vi and emacs as well). */
1056 if (save_files) {
1057 if (!strcmp(verb, "build.build") || !strcmp(verb, "build.build-file") ||
1058 !strcmp(verb, "debug.fix") || !strcmp(verb, "debug.fix-all")) {
1059 workshop_save_files();
1060 }
1061 }
1062
Bram Moolenaar9c13b352005-05-19 20:53:52 +00001063 vim_snprintf(buf, sizeof(buf),
1064 NOCATGETS("toolVerb %s %s %d,%d %d,%d %d,%d %d %s\n"),
Bram Moolenaar071d4272004-06-13 20:20:40 +00001065 verb,
1066 filename,
1067 curLine, curCol,
1068 selStartLine, selStartCol,
1069 selEndLine, selEndCol,
1070 selLength,
1071 selection);
Bram Moolenaar860cae12010-06-05 23:22:07 +02001072 dummy = write(sd, buf, strlen(buf));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001073 if (*selection) {
1074 free(selection);
1075 }
1076 }
1077}
1078
1079/* Send a message to eserve */
Bram Moolenaarba07ce32010-01-06 18:25:34 +01001080#if defined(NOHANDS_SUPPORT_FUNCTIONS) || defined(FEAT_BEVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001081void workshop_send_message(char *buf)
1082{
Bram Moolenaar860cae12010-06-05 23:22:07 +02001083 dummy = write(sd, buf, strlen(buf));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001084}
Bram Moolenaarba07ce32010-01-06 18:25:34 +01001085#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001086
1087/* Some methods, like currentFile, cursorPos, etc. are missing here.
1088 * But it looks like these are used for NoHands testing only so we
1089 * won't bother requiring editors to implement these
1090 */
1091
1092
1093#ifdef DEBUG
1094
1095void
1096pldebug(
1097 char *fmt, /* a printf style format line */
1098 ...)
1099{
1100 va_list ap;
1101
1102 if (dfd != NULL) {
1103 va_start(ap, fmt);
1104 vfprintf(dfd, fmt, ap);
1105 va_end(ap);
1106 fflush(dfd);
1107 }
1108
1109} /* end pldebug */
1110
1111#endif