1 static const char CVSID
[] = "$Id: server.c,v 1.35 2010/07/05 06:23:59 lebert 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 WmClientMsg(TheDisplay
, XtWindow(window
->shell
),
365 "_NET_ACTIVE_WINDOW", 0, 0, 0, 0, 0);
366 XMapRaised(TheDisplay
, XtWindow(window
->shell
));
372 ** Loop over all of the files in the command list
380 /* Read a server command from the input string. Header contains:
381 linenum createFlag fileLen doLen\n, followed by a filename and -do
382 command both followed by newlines. This bit of code reads the
383 header, and converts the newlines following the filename and do
384 command to nulls to terminate the filename and doCommand strings */
385 itemsRead
= sscanf(inPtr
, "%d %d %d %d %d %d %d %d %d%n", &lineNum
,
386 &readFlag
, &createFlag
, &iconicFlag
, &tabbed
, &fileLen
,
387 &doLen
, &lmLen
, &geomLen
, &charsRead
);
390 inPtr
+= charsRead
+ 1;
391 if (inPtr
- string
+ fileLen
> stringLen
)
396 if (inPtr
- string
+ doLen
> stringLen
)
401 if (inPtr
- string
+ lmLen
> stringLen
)
406 if (inPtr
- string
+ geomLen
> stringLen
)
412 /* An empty file name means:
413 * put up an empty, Untitled window, or use an existing one
414 * choose a random window for executing the -do macro upon
417 for (window
=WindowList
; window
!=NULL
; window
=window
->next
)
418 if (!window
->filenameSet
&& !window
->fileChanged
&&
419 isLocatedOnDesktop(window
, currentDesktop
))
422 if (*doCommand
== '\0') {
423 if (window
== NULL
) {
424 EditNewFile(findWindowOnDesktop(tabbed
, currentDesktop
),
425 NULL
, iconicFlag
, lmLen
==0?NULL
:langMode
, NULL
);
428 RaiseDocument(window
);
430 RaiseDocumentWindow(window
);
433 WindowInfo
*win
= WindowList
;
434 /* Starting a new command while another one is still running
435 in the same window is not possible (crashes). */
436 while (win
!= NULL
&& win
->macroCmdData
!= NULL
) {
441 XBell(TheDisplay
, 0);
443 /* Raise before -do (macro could close window). */
447 RaiseDocumentWindow(win
);
448 DoMacro(win
, doCommand
, "-do macro");
455 /* Process the filename by looking for the files in an
456 existing window, or opening if they don't exist */
457 editFlags
= (readFlag
? PREF_READ_ONLY
: 0) | CREATE
|
458 (createFlag
? SUPPRESS_CREATE_WARN
: 0);
459 if (ParseFilename(fullname
, filename
, pathname
) != 0) {
460 fprintf(stderr
, "NEdit: invalid file name\n");
461 deleteFileClosedProperty2(filename
, pathname
);
465 window
= FindWindowWithFile(filename
, pathname
);
466 if (window
== NULL
) {
467 /* Files are opened in background to improve opening speed
468 by defering certain time consuiming task such as syntax
469 highlighting. At the end of the file-opening loop, the
470 last file opened will be raised to restore those deferred
471 items. The current file may also be raised if there're
472 macros to execute on. */
473 window
= EditExistingFile(findWindowOnDesktop(tabbed
, currentDesktop
),
474 filename
, pathname
, editFlags
, geometry
, iconicFlag
,
475 lmLen
== 0 ? NULL
: langMode
,
476 tabbed
== -1? GetPrefOpenInTab() : tabbed
, True
);
479 CleanUpTabBarExposeQueue(window
);
480 if (lastFile
&& window
->shell
!= lastFile
->shell
) {
481 CleanUpTabBarExposeQueue(lastFile
);
482 RaiseDocument(lastFile
);
488 /* Do the actions requested (note DoMacro is last, since the do
489 command can do anything, including closing the window!) */
490 if (window
!= NULL
) {
491 deleteFileOpenProperty(window
);
492 getFileClosedProperty(window
);
495 SelectNumberedLine(window
, lineNum
);
497 if (*doCommand
!= '\0') {
498 RaiseDocument(window
);
501 WmClientMsg(TheDisplay
, XtWindow(window
->shell
),
502 "_NET_ACTIVE_WINDOW", 0, 0, 0, 0, 0);
503 XMapRaised(TheDisplay
, XtWindow(window
->shell
));
506 /* Starting a new command while another one is still running
507 in the same window is not possible (crashes). */
508 if (window
->macroCmdData
!= NULL
) {
509 XBell(TheDisplay
, 0);
511 DoMacro(window
, doCommand
, "-do macro");
512 /* in case window is closed by macro functions
513 such as close() or detach_document() */
514 if (!IsValidWindow(window
))
516 if (lastFile
&& !IsValidWindow(lastFile
))
521 /* register the last file opened for later use */
524 lastIconic
= iconicFlag
;
527 deleteFileOpenProperty2(filename
, pathname
);
528 deleteFileClosedProperty2(filename
, pathname
);
532 /* Raise the last file opened */
534 CleanUpTabBarExposeQueue(lastFile
);
536 RaiseDocument(lastFile
);
538 RaiseDocumentWindow(lastFile
);
544 fprintf(stderr
, "NEdit: error processing server request\n");