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