1 static const char CVSID
[] = "$Id: server.c,v 1.34 2007/12/31 11:12:43 yooden 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 *
11 * version. In addition, you may distribute version of this program linked to *
12 * Motif or Open Motif. See README for details. *
14 * This software is distributed in the hope that it will be useful, but WITHOUT *
15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or *
16 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License *
19 * You should have received a copy of the GNU General Public License along with *
20 * software; if not, write to the Free Software Foundation, Inc., 59 Temple *
21 * Place, Suite 330, Boston, MA 02111-1307 USA *
23 * Nirvana Text Editor *
26 * Written by Mark Edel *
28 *******************************************************************************/
31 #include "../config.h"
39 #include "selection.h"
42 #include "preferences.h"
43 #include "server_common.h"
44 #include "../util/fileUtils.h"
45 #include "../util/utils.h"
46 #include "../util/misc.h"
53 #include <lib$routines.h>
56 #include "../util/VMSparam.h"
57 #include "../util/VMSutils.h"
59 #include <sys/types.h>
60 #include <sys/utsname.h>
62 #include <sys/param.h>
76 static void processServerCommand(void);
77 static void cleanUpServerCommunication(void);
78 static void processServerCommandString(char *string
);
79 static void getFileClosedProperty(WindowInfo
*window
);
80 static int isLocatedOnDesktop(WindowInfo
*window
, long currentDesktop
);
81 static WindowInfo
*findWindowOnDesktop(int tabbed
, long currentDesktop
);
83 static Atom ServerRequestAtom
= 0;
84 static Atom ServerExistsAtom
= 0;
87 ** Set up inter-client communication for NEdit server end, expected to be
88 ** called only once at startup time
90 void InitServerCommunication(void)
92 Window rootWindow
= RootWindow(TheDisplay
, DefaultScreen(TheDisplay
));
94 /* Create the server property atoms on the current DISPLAY. */
95 CreateServerPropertyAtoms(GetPrefServerName(),
99 /* Pay attention to PropertyChangeNotify events on the root window.
100 Do this before putting up the server atoms, to avoid a race
101 condition (when nc sees that the server exists, it sends a command,
102 so we must make sure that we already monitor properties). */
103 XSelectInput(TheDisplay
, rootWindow
, PropertyChangeMask
);
105 /* Create the server-exists property on the root window to tell clients
106 whether to try a request (otherwise clients would always have to
107 try and wait for their timeouts to expire) */
108 XChangeProperty(TheDisplay
, rootWindow
, ServerExistsAtom
, XA_STRING
, 8,
109 PropModeReplace
, (unsigned char *)"True", 4);
111 /* Set up exit handler for cleaning up server-exists property */
112 atexit(cleanUpServerCommunication
);
115 static void deleteProperty(Atom
* atom
)
123 XDeleteProperty(TheDisplay
,
124 RootWindow(TheDisplay
, DefaultScreen(TheDisplay
)),
131 ** Exit handler. Removes server-exists property on root window
133 static void cleanUpServerCommunication(void)
137 /* Delete any per-file properties that still exist
138 * (and that server knows about)
140 for (w
= WindowList
; w
; w
= w
->next
) {
141 DeleteFileClosedProperty(w
);
144 /* Delete any per-file properties that still exist
145 * (but that that server doesn't know about)
147 DeleteServerFileAtoms(GetPrefServerName(),
148 RootWindow(TheDisplay
, DefaultScreen(TheDisplay
)));
150 /* Delete the server-exists property from the root window (if it was
151 assigned) and don't let the process exit until the X server has
152 processed the delete request (otherwise it won't be done) */
153 deleteProperty(&ServerExistsAtom
);
154 XSync(TheDisplay
, False
);
158 ** Special event loop for NEdit servers. Processes PropertyNotify events on
159 ** the root window (this would not be necessary if it were possible to
160 ** register an Xt event-handler for a window, rather than only a widget).
161 ** Invokes server routines when a server-request property appears,
162 ** re-establishes server-exists property when another server exits and
163 ** this server is still alive to take over.
165 void ServerMainLoop(XtAppContext context
)
169 XtAppNextEvent(context
, &event
);
170 ServerDispatchEvent(&event
);
174 static void processServerCommand(void)
177 unsigned long nItems
, dummyULong
;
178 unsigned char *propValue
;
181 /* Get the value of the property, and delete it from the root window */
182 if (XGetWindowProperty(TheDisplay
, RootWindow(TheDisplay
,
183 DefaultScreen(TheDisplay
)), ServerRequestAtom
, 0, INT_MAX
, True
,
184 XA_STRING
, &dummyAtom
, &getFmt
, &nItems
, &dummyULong
, &propValue
)
185 != Success
|| getFmt
!= 8)
188 /* Invoke the command line processor on the string to process the request */
189 processServerCommandString((char *)propValue
);
193 Boolean
ServerDispatchEvent(XEvent
*event
)
196 Window rootWindow
= RootWindow(TheDisplay
, DefaultScreen(TheDisplay
));
197 if (event
->xany
.window
== rootWindow
&& event
->xany
.type
== PropertyNotify
) {
198 const XPropertyEvent
* e
= &event
->xproperty
;
200 if (e
->type
== PropertyNotify
&& e
->window
== rootWindow
) {
201 if (e
->atom
== ServerRequestAtom
&& e
->state
== PropertyNewValue
)
202 processServerCommand();
203 else if (e
->atom
== ServerExistsAtom
&& e
->state
== PropertyDelete
)
204 XChangeProperty(TheDisplay
,
206 ServerExistsAtom
, XA_STRING
,
208 (unsigned char *)"True", 4);
212 return XtDispatchEvent(event
);
215 /* Try to find existing 'FileOpen' property atom for path. */
216 static Atom
findFileOpenProperty(const char* filename
,
217 const char* pathname
) {
218 char path
[MAXPATHLEN
];
221 if (!IsServer
) return(None
);
223 strcpy(path
, pathname
);
224 strcat(path
, filename
);
225 atom
= CreateServerFileOpenAtom(GetPrefServerName(), path
);
229 /* Destroy the 'FileOpen' atom to inform nc that this file has
232 static void deleteFileOpenProperty(WindowInfo
*window
)
234 if (window
->filenameSet
) {
235 Atom atom
= findFileOpenProperty(window
->filename
, window
->path
);
236 deleteProperty(&atom
);
240 static void deleteFileOpenProperty2(const char* filename
,
241 const char* pathname
)
243 Atom atom
= findFileOpenProperty(filename
, pathname
);
244 deleteProperty(&atom
);
249 /* Try to find existing 'FileClosed' property atom for path. */
250 static Atom
findFileClosedProperty(const char* filename
,
251 const char* pathname
)
253 char path
[MAXPATHLEN
];
256 if (!IsServer
) return(None
);
258 strcpy(path
, pathname
);
259 strcat(path
, filename
);
260 atom
= CreateServerFileClosedAtom(GetPrefServerName(),
262 True
); /* don't create */
266 /* Get hold of the property to use when closing the file. */
267 static void getFileClosedProperty(WindowInfo
*window
)
269 if (window
->filenameSet
) {
270 window
->fileClosedAtom
= findFileClosedProperty(window
->filename
,
275 /* Delete the 'FileClosed' atom to inform nc that this file has
278 void DeleteFileClosedProperty(WindowInfo
*window
)
280 if (window
->filenameSet
) {
281 deleteProperty(&window
->fileClosedAtom
);
285 static void deleteFileClosedProperty2(const char* filename
,
286 const char* pathname
)
288 Atom atom
= findFileClosedProperty(filename
, pathname
);
289 deleteProperty(&atom
);
292 static int isLocatedOnDesktop(WindowInfo
*window
, long currentDesktop
)
295 if (currentDesktop
== -1)
296 return True
; /* No desktop information available */
298 windowDesktop
= QueryDesktop(TheDisplay
, window
->shell
);
299 /* Sticky windows have desktop 0xFFFFFFFF by convention */
300 if (windowDesktop
== currentDesktop
|| windowDesktop
== 0xFFFFFFFFL
)
301 return True
; /* Desktop matches, or window is sticky */
306 static WindowInfo
*findWindowOnDesktop(int tabbed
, long currentDesktop
)
310 if (tabbed
== 0 || (tabbed
== -1 && GetPrefOpenInTab() == 0)) {
311 /* A new window is requested, unless we find an untitled unmodified
312 document on the current desktop */
313 for (window
=WindowList
; window
!=NULL
; window
=window
->next
) {
314 if (window
->filenameSet
|| window
->fileChanged
||
315 window
->macroCmdData
!= NULL
) {
318 /* No check for top document here! */
319 if (isLocatedOnDesktop(window
, currentDesktop
)) {
324 /* Find a window on the current desktop to hold the new document */
325 for (window
=WindowList
; window
!=NULL
; window
=window
->next
) {
326 /* Avoid unnecessary property access (server round-trip) */
327 if (!IsTopDocument(window
)) {
330 if (isLocatedOnDesktop(window
, currentDesktop
)) {
336 return NULL
; /* No window found on current desktop -> create new window */
339 static void processServerCommandString(char *string
)
341 char *fullname
, filename
[MAXPATHLEN
], pathname
[MAXPATHLEN
];
342 char *doCommand
, *geometry
, *langMode
, *inPtr
;
343 int editFlags
, stringLen
= strlen(string
);
344 int lineNum
, createFlag
, readFlag
, iconicFlag
, lastIconic
= 0, tabbed
= -1;
345 int fileLen
, doLen
, lmLen
, geomLen
, charsRead
, itemsRead
;
346 WindowInfo
*window
, *lastFile
= NULL
;
347 long currentDesktop
= QueryCurrentDesktop(TheDisplay
,
348 RootWindow(TheDisplay
, DefaultScreen(TheDisplay
)));
350 /* If the command string is empty, put up an empty, Untitled window
351 (or just pop one up if it already exists) */
352 if (string
[0] == '\0') {
353 for (window
=WindowList
; window
!=NULL
; window
=window
->next
)
354 if (!window
->filenameSet
&& !window
->fileChanged
&&
355 isLocatedOnDesktop(window
, currentDesktop
))
357 if (window
== NULL
) {
358 EditNewFile(findWindowOnDesktop(tabbed
, currentDesktop
), NULL
,
363 RaiseDocument(window
);
364 XMapRaised(TheDisplay
, XtWindow(window
->shell
));
370 ** Loop over all of the files in the command list
378 /* Read a server command from the input string. Header contains:
379 linenum createFlag fileLen doLen\n, followed by a filename and -do
380 command both followed by newlines. This bit of code reads the
381 header, and converts the newlines following the filename and do
382 command to nulls to terminate the filename and doCommand strings */
383 itemsRead
= sscanf(inPtr
, "%d %d %d %d %d %d %d %d %d%n", &lineNum
,
384 &readFlag
, &createFlag
, &iconicFlag
, &tabbed
, &fileLen
,
385 &doLen
, &lmLen
, &geomLen
, &charsRead
);
388 inPtr
+= charsRead
+ 1;
389 if (inPtr
- string
+ fileLen
> stringLen
)
394 if (inPtr
- string
+ doLen
> stringLen
)
399 if (inPtr
- string
+ lmLen
> stringLen
)
404 if (inPtr
- string
+ geomLen
> stringLen
)
410 /* An empty file name means:
411 * put up an empty, Untitled window, or use an existing one
412 * choose a random window for executing the -do macro upon
415 for (window
=WindowList
; window
!=NULL
; window
=window
->next
)
416 if (!window
->filenameSet
&& !window
->fileChanged
&&
417 isLocatedOnDesktop(window
, currentDesktop
))
420 if (*doCommand
== '\0') {
421 if (window
== NULL
) {
422 EditNewFile(findWindowOnDesktop(tabbed
, currentDesktop
),
423 NULL
, iconicFlag
, lmLen
==0?NULL
:langMode
, NULL
);
426 RaiseDocument(window
);
428 RaiseDocumentWindow(window
);
431 WindowInfo
*win
= WindowList
;
432 /* Starting a new command while another one is still running
433 in the same window is not possible (crashes). */
434 while (win
!= NULL
&& win
->macroCmdData
!= NULL
) {
439 XBell(TheDisplay
, 0);
441 /* Raise before -do (macro could close window). */
445 RaiseDocumentWindow(win
);
446 DoMacro(win
, doCommand
, "-do macro");
453 /* Process the filename by looking for the files in an
454 existing window, or opening if they don't exist */
455 editFlags
= (readFlag
? PREF_READ_ONLY
: 0) | CREATE
|
456 (createFlag
? SUPPRESS_CREATE_WARN
: 0);
457 if (ParseFilename(fullname
, filename
, pathname
) != 0) {
458 fprintf(stderr
, "NEdit: invalid file name\n");
459 deleteFileClosedProperty2(filename
, pathname
);
463 window
= FindWindowWithFile(filename
, pathname
);
464 if (window
== NULL
) {
465 /* Files are opened in background to improve opening speed
466 by defering certain time consuiming task such as syntax
467 highlighting. At the end of the file-opening loop, the
468 last file opened will be raised to restore those deferred
469 items. The current file may also be raised if there're
470 macros to execute on. */
471 window
= EditExistingFile(findWindowOnDesktop(tabbed
, currentDesktop
),
472 filename
, pathname
, editFlags
, geometry
, iconicFlag
,
473 lmLen
== 0 ? NULL
: langMode
,
474 tabbed
== -1? GetPrefOpenInTab() : tabbed
, True
);
477 CleanUpTabBarExposeQueue(window
);
478 if (lastFile
&& window
->shell
!= lastFile
->shell
) {
479 CleanUpTabBarExposeQueue(lastFile
);
480 RaiseDocument(lastFile
);
486 /* Do the actions requested (note DoMacro is last, since the do
487 command can do anything, including closing the window!) */
488 if (window
!= NULL
) {
489 deleteFileOpenProperty(window
);
490 getFileClosedProperty(window
);
493 SelectNumberedLine(window
, lineNum
);
495 if (*doCommand
!= '\0') {
496 RaiseDocument(window
);
499 XMapRaised(TheDisplay
, XtWindow(window
->shell
));
501 /* Starting a new command while another one is still running
502 in the same window is not possible (crashes). */
503 if (window
->macroCmdData
!= NULL
) {
504 XBell(TheDisplay
, 0);
506 DoMacro(window
, doCommand
, "-do macro");
507 /* in case window is closed by macro functions
508 such as close() or detach_document() */
509 if (!IsValidWindow(window
))
511 if (lastFile
&& !IsValidWindow(lastFile
))
516 /* register the last file opened for later use */
519 lastIconic
= iconicFlag
;
522 deleteFileOpenProperty2(filename
, pathname
);
523 deleteFileClosedProperty2(filename
, pathname
);
527 /* Raise the last file opened */
529 CleanUpTabBarExposeQueue(lastFile
);
531 RaiseDocument(lastFile
);
533 RaiseDocumentWindow(lastFile
);
539 fprintf(stderr
, "NEdit: error processing server request\n");