1 static const char CVSID
[] = "$Id: server.c,v 1.30 2004/06/23 13:25:56 edg Exp $";
2 /*******************************************************************************
4 * server.c -- Nirvana Editor edit-server component *
6 * Copyright (C) 1999 Mark Edel *
8 * This is free software; you can redistribute it and/or modify it under the *
9 * terms of the GNU General Public License as published by the Free Software *
10 * Foundation; either version 2 of the License, or (at your option) any later *
13 * This software is distributed in the hope that it will be useful, but WITHOUT *
14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or *
15 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License *
18 * You should have received a copy of the GNU General Public License along with *
19 * software; if not, write to the Free Software Foundation, Inc., 59 Temple *
20 * Place, Suite 330, Boston, MA 02111-1307 USA *
22 * Nirvana Text Editor *
25 * Written by Mark Edel *
27 *******************************************************************************/
30 #include "../config.h"
38 #include "selection.h"
41 #include "preferences.h"
42 #include "server_common.h"
43 #include "../util/fileUtils.h"
44 #include "../util/utils.h"
45 #include "../util/misc.h"
52 #include <lib$routines.h>
55 #include "../util/VMSparam.h"
56 #include "../util/VMSutils.h"
58 #include <sys/types.h>
59 #include <sys/utsname.h>
61 #include <sys/param.h>
75 static void processServerCommand(void);
76 static void cleanUpServerCommunication(void);
77 static void processServerCommandString(char *string
);
78 static void getFileClosedProperty(WindowInfo
*window
);
79 static int isLocatedOnDesktop(WindowInfo
*window
, long currentDesktop
);
80 static WindowInfo
*findWindowOnDesktop(int tabbed
, long currentDesktop
);
82 static Atom ServerRequestAtom
= 0;
83 static Atom ServerExistsAtom
= 0;
86 ** Set up inter-client communication for NEdit server end, expected to be
87 ** called only once at startup time
89 void InitServerCommunication(void)
91 Window rootWindow
= RootWindow(TheDisplay
, DefaultScreen(TheDisplay
));
93 /* Create the server property atoms on the current DISPLAY. */
94 CreateServerPropertyAtoms(GetPrefServerName(),
98 /* Pay attention to PropertyChangeNotify events on the root window.
99 Do this before putting up the server atoms, to avoid a race
100 condition (when nc sees that the server exists, it sends a command,
101 so we must make sure that we already monitor properties). */
102 XSelectInput(TheDisplay
, rootWindow
, PropertyChangeMask
);
104 /* Create the server-exists property on the root window to tell clients
105 whether to try a request (otherwise clients would always have to
106 try and wait for their timeouts to expire) */
107 XChangeProperty(TheDisplay
, rootWindow
, ServerExistsAtom
, XA_STRING
, 8,
108 PropModeReplace
, (unsigned char *)"True", 4);
110 /* Set up exit handler for cleaning up server-exists property */
111 atexit(cleanUpServerCommunication
);
114 static void deleteProperty(Atom
* atom
)
122 XDeleteProperty(TheDisplay
,
123 RootWindow(TheDisplay
, DefaultScreen(TheDisplay
)),
130 ** Exit handler. Removes server-exists property on root window
132 static void cleanUpServerCommunication(void)
136 /* Delete any per-file properties that still exist
137 * (and that server knows about)
139 for (w
= WindowList
; w
; w
= w
->next
) {
140 DeleteFileClosedProperty(w
);
143 /* Delete any per-file properties that still exist
144 * (but that that server doesn't know about)
146 DeleteServerFileAtoms(GetPrefServerName(),
147 RootWindow(TheDisplay
, DefaultScreen(TheDisplay
)));
149 /* Delete the server-exists property from the root window (if it was
150 assigned) and don't let the process exit until the X server has
151 processed the delete request (otherwise it won't be done) */
152 deleteProperty(&ServerExistsAtom
);
153 XSync(TheDisplay
, False
);
157 ** Special event loop for NEdit servers. Processes PropertyNotify events on
158 ** the root window (this would not be necessary if it were possible to
159 ** register an Xt event-handler for a window, rather than only a widget).
160 ** Invokes server routines when a server-request property appears,
161 ** re-establishes server-exists property when another server exits and
162 ** this server is still alive to take over.
164 void ServerMainLoop(XtAppContext context
)
168 XtAppNextEvent(context
, &event
);
169 ServerDispatchEvent(&event
);
173 static void processServerCommand(void)
176 unsigned long nItems
, dummyULong
;
177 unsigned char *propValue
;
180 /* Get the value of the property, and delete it from the root window */
181 if (XGetWindowProperty(TheDisplay
, RootWindow(TheDisplay
,
182 DefaultScreen(TheDisplay
)), ServerRequestAtom
, 0, INT_MAX
, True
,
183 XA_STRING
, &dummyAtom
, &getFmt
, &nItems
, &dummyULong
, &propValue
)
184 != Success
|| getFmt
!= 8)
187 /* Invoke the command line processor on the string to process the request */
188 processServerCommandString((char *)propValue
);
192 Boolean
ServerDispatchEvent(XEvent
*event
)
195 Window rootWindow
= RootWindow(TheDisplay
, DefaultScreen(TheDisplay
));
196 if (event
->xany
.window
== rootWindow
&& event
->xany
.type
== PropertyNotify
) {
197 const XPropertyEvent
* e
= &event
->xproperty
;
199 if (e
->type
== PropertyNotify
&& e
->window
== rootWindow
) {
200 if (e
->atom
== ServerRequestAtom
&& e
->state
== PropertyNewValue
)
201 processServerCommand();
202 else if (e
->atom
== ServerExistsAtom
&& e
->state
== PropertyDelete
)
203 XChangeProperty(TheDisplay
,
205 ServerExistsAtom
, XA_STRING
,
207 (unsigned char *)"True", 4);
211 return XtDispatchEvent(event
);
214 /* Try to find existing 'FileOpen' property atom for path. */
215 static Atom
findFileOpenProperty(const char* filename
,
216 const char* pathname
) {
217 char path
[MAXPATHLEN
];
220 if (!IsServer
) return(None
);
222 strcpy(path
, pathname
);
223 strcat(path
, filename
);
224 atom
= CreateServerFileOpenAtom(GetPrefServerName(), path
);
228 /* Destroy the 'FileOpen' atom to inform nc that this file has
231 static void deleteFileOpenProperty(WindowInfo
*window
)
233 if (window
->filenameSet
) {
234 Atom atom
= findFileOpenProperty(window
->filename
, window
->path
);
235 deleteProperty(&atom
);
239 static void deleteFileOpenProperty2(const char* filename
,
240 const char* pathname
)
242 Atom atom
= findFileOpenProperty(filename
, pathname
);
243 deleteProperty(&atom
);
248 /* Try to find existing 'FileClosed' property atom for path. */
249 static Atom
findFileClosedProperty(const char* filename
,
250 const char* pathname
)
252 char path
[MAXPATHLEN
];
255 if (!IsServer
) return(None
);
257 strcpy(path
, pathname
);
258 strcat(path
, filename
);
259 atom
= CreateServerFileClosedAtom(GetPrefServerName(),
261 True
); /* don't create */
265 /* Get hold of the property to use when closing the file. */
266 static void getFileClosedProperty(WindowInfo
*window
)
268 if (window
->filenameSet
) {
269 window
->fileClosedAtom
= findFileClosedProperty(window
->filename
,
274 /* Delete the 'FileClosed' atom to inform nc that this file has
277 void DeleteFileClosedProperty(WindowInfo
*window
)
279 if (window
->filenameSet
) {
280 deleteProperty(&window
->fileClosedAtom
);
284 static void deleteFileClosedProperty2(const char* filename
,
285 const char* pathname
)
287 Atom atom
= findFileClosedProperty(filename
, pathname
);
288 deleteProperty(&atom
);
291 static int isLocatedOnDesktop(WindowInfo
*window
, long currentDesktop
)
294 if (currentDesktop
== -1)
295 return True
; /* No desktop information available */
297 windowDesktop
= QueryDesktop(TheDisplay
, window
->shell
);
298 /* Sticky windows have desktop 0xFFFFFFFF by convention */
299 if (windowDesktop
== currentDesktop
|| windowDesktop
== 0xFFFFFFFFL
)
300 return True
; /* Desktop matches, or window is sticky */
305 static WindowInfo
*findWindowOnDesktop(int tabbed
, long currentDesktop
)
309 if (currentDesktop
== -1) /* No desktop information available */
312 if (tabbed
== 0 || (tabbed
== -1 && GetPrefOpenInTab() == 0)) {
313 /* A new window is requested, unless we find an untitled unmodified
314 document on the current desktop */
315 for (window
=WindowList
; window
!=NULL
; window
=window
->next
) {
316 if (window
->filenameSet
|| window
->fileChanged
||
317 window
->macroCmdData
!= NULL
)
319 /* No check for top document here! */
320 if (isLocatedOnDesktop(window
, currentDesktop
))
325 /* Find a window on the current desktop to hold the new document */
326 for (window
=WindowList
; window
!=NULL
; window
=window
->next
) {
327 /* Avoid unnecessary property access (server round-trip) */
328 if (!IsTopDocument(window
))
330 if (isLocatedOnDesktop(window
, currentDesktop
))
334 return NULL
; /* No window found on current desktop -> create new window */
337 static void processServerCommandString(char *string
)
339 char *fullname
, filename
[MAXPATHLEN
], pathname
[MAXPATHLEN
];
340 char *doCommand
, *geometry
, *langMode
, *inPtr
;
341 int editFlags
, stringLen
= strlen(string
);
342 int lineNum
, createFlag
, readFlag
, iconicFlag
, lastIconic
= 0, tabbed
;
343 int fileLen
, doLen
, lmLen
, geomLen
, charsRead
, itemsRead
;
344 WindowInfo
*window
, *lastFile
= NULL
;
345 long currentDesktop
= QueryCurrentDesktop(TheDisplay
,
346 RootWindow(TheDisplay
, DefaultScreen(TheDisplay
)));
348 /* If the command string is empty, put up an empty, Untitled window
349 (or just pop one up if it already exists) */
350 if (string
[0] == '\0') {
351 for (window
=WindowList
; window
!=NULL
; window
=window
->next
)
352 if (!window
->filenameSet
&& !window
->fileChanged
&&
353 isLocatedOnDesktop(window
, currentDesktop
))
355 if (window
== NULL
) {
356 EditNewFile(findWindowOnDesktop(tabbed
, currentDesktop
), NULL
,
361 RaiseDocument(window
);
362 XMapRaised(TheDisplay
, XtWindow(window
->shell
));
368 ** Loop over all of the files in the command list
376 /* Read a server command from the input string. Header contains:
377 linenum createFlag fileLen doLen\n, followed by a filename and -do
378 command both followed by newlines. This bit of code reads the
379 header, and converts the newlines following the filename and do
380 command to nulls to terminate the filename and doCommand strings */
381 itemsRead
= sscanf(inPtr
, "%d %d %d %d %d %d %d %d %d%n", &lineNum
,
382 &readFlag
, &createFlag
, &iconicFlag
, &tabbed
, &fileLen
,
383 &doLen
, &lmLen
, &geomLen
, &charsRead
);
386 inPtr
+= charsRead
+ 1;
387 if (inPtr
- string
+ fileLen
> stringLen
)
392 if (inPtr
- string
+ doLen
> stringLen
)
397 if (inPtr
- string
+ lmLen
> stringLen
)
402 if (inPtr
- string
+ geomLen
> stringLen
)
408 /* An empty file name means:
409 * put up an empty, Untitled window, or use an existing one
410 * choose a random window for executing the -do macro upon
413 for (window
=WindowList
; window
!=NULL
; window
=window
->next
)
414 if (!window
->filenameSet
&& !window
->fileChanged
&&
415 isLocatedOnDesktop(window
, currentDesktop
))
418 if (*doCommand
== '\0') {
419 if (window
== NULL
) {
420 EditNewFile(findWindowOnDesktop(tabbed
, currentDesktop
),
421 NULL
, iconicFlag
, lmLen
==0?NULL
:langMode
, NULL
);
424 RaiseDocument(window
);
426 RaiseDocumentWindow(window
);
429 WindowInfo
*win
= WindowList
;
430 /* Starting a new command while another one is still running
431 in the same window is not possible (crashes). */
432 while (win
!= NULL
&& win
->macroCmdData
!= NULL
) {
437 XBell(TheDisplay
, 0);
439 /* Raise before -do (macro could close window). */
443 RaiseDocumentWindow(win
);
444 DoMacro(win
, doCommand
, "-do macro");
451 /* Process the filename by looking for the files in an
452 existing window, or opening if they don't exist */
453 editFlags
= (readFlag
? PREF_READ_ONLY
: 0) | CREATE
|
454 (createFlag
? SUPPRESS_CREATE_WARN
: 0);
455 if (ParseFilename(fullname
, filename
, pathname
) != 0) {
456 fprintf(stderr
, "NEdit: invalid file name\n");
457 deleteFileClosedProperty2(filename
, pathname
);
461 window
= FindWindowWithFile(filename
, pathname
);
462 if (window
== NULL
) {
463 /* Files are opened in background to improve opening speed
464 by defering certain time consuiming task such as syntax
465 highlighting. At the end of the file-opening loop, the
466 last file opened will be raised to restore those deferred
467 items. The current file may also be raised if there're
468 macros to execute on. */
469 window
= EditExistingFile(findWindowOnDesktop(tabbed
, currentDesktop
),
470 filename
, pathname
, editFlags
, geometry
, iconicFlag
,
471 lmLen
== 0 ? NULL
: langMode
,
472 tabbed
== -1? GetPrefOpenInTab() : tabbed
, True
);
475 CleanUpTabBarExposeQueue(window
);
476 if (lastFile
&& window
->shell
!= lastFile
->shell
) {
477 CleanUpTabBarExposeQueue(lastFile
);
478 RaiseDocument(lastFile
);
484 /* Do the actions requested (note DoMacro is last, since the do
485 command can do anything, including closing the window!) */
486 if (window
!= NULL
) {
487 deleteFileOpenProperty(window
);
488 getFileClosedProperty(window
);
491 SelectNumberedLine(window
, lineNum
);
493 if (*doCommand
!= '\0') {
494 RaiseDocument(window
);
497 XMapRaised(TheDisplay
, XtWindow(window
->shell
));
499 /* Starting a new command while another one is still running
500 in the same window is not possible (crashes). */
501 if (window
->macroCmdData
!= NULL
) {
502 XBell(TheDisplay
, 0);
504 DoMacro(window
, doCommand
, "-do macro");
505 /* in case window is closed by macro functions
506 such as close() or detach_document() */
507 if (!IsValidWindow(window
))
509 if (lastFile
&& !IsValidWindow(lastFile
))
514 /* register the last file opened for later use */
517 lastIconic
= iconicFlag
;
520 deleteFileOpenProperty2(filename
, pathname
);
521 deleteFileClosedProperty2(filename
, pathname
);
525 /* Raise the last file opened */
527 CleanUpTabBarExposeQueue(lastFile
);
529 RaiseDocument(lastFile
);
531 RaiseDocumentWindow(lastFile
);
537 fprintf(stderr
, "NEdit: error processing server request\n");