home *** CD-ROM | disk | FTP | other *** search
/ PC Pro 2002 April / pcpro0402.iso / essentials / graphics / Gimp / gimp-src-20001226.exe / src / gimp / plug-ins / webbrowser / webbrowser.c < prev   
Encoding:
C/C++ Source or Header  |  2000-09-29  |  26.7 KB  |  971 lines

  1. /* The GIMP -- an image manipulation program
  2.  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
  3.  * Copyright (C) 1997 Misha Dynin
  4.  *
  5.  * This program is free software; you can redistribute it and/or modify
  6.  * it under the terms of the GNU General Public License as published by
  7.  * the Free Software Foundation; either version 2 of the License, or
  8.  * (at your option) any later version.
  9.  *
  10.  * This program is distributed in the hope that it will be useful,
  11.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.  * GNU General Public License for more details.
  14.  *
  15.  * You should have received a copy of the GNU General Public License
  16.  * along with this program; if not, write to the Free Software
  17.  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  18.  */
  19.  
  20. /*
  21.     Web Browser v0.3 -- opens a URL in Netscape
  22.  
  23.         by Misha Dynin <misha@xcf.berkeley.edu>
  24.  
  25.     For more information, see webbrowser.readme, as well as
  26.  
  27.         http://www.xcf.berkeley.edu/~misha/gimp/
  28.  
  29.  */
  30.  
  31. #include "config.h"
  32.  
  33. #include <stdlib.h>
  34. #include <stdio.h>
  35. #include <string.h>
  36. #ifdef HAVE_UNISTD_H
  37. #include <unistd.h>
  38. #endif
  39. #include <errno.h>
  40. #include <ctype.h>
  41. #ifdef __EMX__
  42. #include <process.h>
  43. #endif
  44.  
  45. #include <gtk/gtk.h>
  46.  
  47. #ifdef G_OS_WIN32
  48. #define STRICT
  49. #include <windows.h>
  50. #include <shellapi.h>
  51. #else
  52. #include <X11/X.h>
  53. #include <X11/Xatom.h>
  54. #include <X11/Xlib.h>
  55. #include <X11/Xmu/WinUtil.h>    /* for XmuClientWindow() */
  56. #endif
  57.  
  58. #include <libgimp/gimp.h>
  59. #include <libgimp/gimpui.h>
  60.  
  61. #include "libgimp/stdplugins-intl.h"
  62.  
  63.  
  64. /* Browser program name -- start in case it's not already running */
  65. #define BROWSER_PROGNAME    "netscape"
  66.  
  67. /* Open a new window with request? */
  68. #define OPEN_URL_NEW_WINDOW    1
  69. #define OPEN_URL_CURRENT_WINDOW    0
  70.  
  71. #define URL_BUF_SIZE 255
  72.  
  73.  
  74. static void query (void);
  75. static void run   (gchar   *name,
  76.            gint     nparams,
  77.            GimpParam  *param,
  78.            gint    *nreturn_vals,
  79.            GimpParam **return_vals);
  80.  
  81. static gint open_url_dialog     (void);
  82. static void ok_callback         (GtkWidget *widget,
  83.                  gpointer   data);
  84. static void about_callback      (GtkWidget *widget,
  85.                  gpointer   data);
  86. static void new_window_callback (GtkWidget *widget,
  87.                  gpointer   data);
  88. static void url_callback        (GtkWidget *widget,
  89.                  gpointer   data);
  90. #ifndef G_OS_WIN32
  91. static gint mozilla_remote      (gchar     *command);
  92. #endif
  93.  
  94. static gint open_url            (gchar     *url,
  95.                  gint       new_window);
  96.  
  97.  
  98. GimpPlugInInfo PLUG_IN_INFO =
  99. {
  100.   NULL,  /* init_proc  */
  101.   NULL,  /* quit_proc  */
  102.   query, /* query_proc */
  103.   run,   /* run_proc   */
  104. };
  105.  
  106. typedef struct
  107. {
  108.   gchar url[URL_BUF_SIZE];
  109.   gint  new_window;
  110. } u_info;
  111.  
  112. static u_info url_info =
  113. {
  114.   "http://www.gimp.org/",  /* Default URL */
  115.   OPEN_URL_NEW_WINDOW,     /* Change to ...CURRENT_WINDOW if
  116.                 * you prefer that as the default
  117.                 */
  118. };
  119.  
  120. static gboolean run_flag = FALSE;
  121.  
  122. MAIN ()
  123.  
  124. static void
  125. query (void)
  126. {
  127.   static GimpParamDef args[] =
  128.   {
  129.     { GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive" },
  130.     { GIMP_PDB_STRING, "url", "URL of a document to open" },
  131.     { GIMP_PDB_INT32,  "new_window", "Create a new window or use existing one?" },
  132.   };
  133.   static gint nargs = sizeof (args) / sizeof (args[0]);
  134.  
  135.   gimp_install_procedure ("extension_web_browser",
  136.               "open URL in Netscape",
  137.               "You need to have Netscape installed",
  138.               "Misha Dynin <misha@xcf.berkeley.edu>",
  139.               "Misha Dynin, Jamie Zawinski, Spencer Kimball & Peter Mattis",
  140.               "1997",
  141.               N_("<Toolbox>/Xtns/Web Browser/Open URL..."),
  142.               NULL,
  143.               GIMP_EXTENSION,
  144.               nargs, 0,
  145.               args, NULL);
  146. }
  147.  
  148. static void
  149. run (gchar   *name,
  150.      gint     nparams,
  151.      GimpParam  *param,
  152.      gint    *nreturn_vals,
  153.      GimpParam **return_vals)
  154. {
  155.   static GimpParam values[1];
  156.   GimpRunModeType run_mode;
  157.   GimpPDBStatusType status = GIMP_PDB_SUCCESS;
  158.  
  159.   run_mode = param[0].data.d_int32;
  160.  
  161.   values[0].type = GIMP_PDB_STATUS;
  162.   values[0].data.d_status = status;
  163.  
  164.   *nreturn_vals = 1;
  165.   *return_vals  = values;
  166.  
  167.   if (strcmp (name, "extension_web_browser") == 0)
  168.     {
  169.       switch (run_mode)
  170.     {
  171.     case GIMP_RUN_INTERACTIVE:
  172.       INIT_I18N_UI ();
  173.       /* Possibly retrieve data */
  174.       gimp_get_data ("extension_web_browser", &url_info);
  175.  
  176.       if (!open_url_dialog ())
  177.         return;
  178.       break;
  179.  
  180.     case GIMP_RUN_NONINTERACTIVE:
  181.       /*  Make sure all the arguments are there!  */
  182.       if (nparams != 3)
  183.         {
  184.           status = GIMP_PDB_CALLING_ERROR;
  185.         }
  186.       else
  187.         {
  188.           strncpy (url_info.url, param[1].data.d_string, URL_BUF_SIZE);
  189.           url_info.new_window = param[2].data.d_int32;
  190.         }
  191.       break;
  192.  
  193.     case GIMP_RUN_WITH_LAST_VALS:
  194.       gimp_get_data ("extension_web_browser", &url_info);
  195.       break;
  196.  
  197.     default:
  198.       break;
  199.     }
  200.  
  201.       if (status == GIMP_PDB_SUCCESS)
  202.     {
  203.       if (!open_url (url_info.url, url_info.new_window))
  204.         values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
  205.  
  206.       if (run_mode == GIMP_RUN_INTERACTIVE)
  207.         gimp_set_data ("extension_web_browser", &url_info, sizeof (u_info));
  208.  
  209.       values[0].data.d_status = GIMP_PDB_SUCCESS;
  210.     }
  211.       else
  212.     values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
  213.     }
  214.   else
  215.     g_assert_not_reached ();
  216. }
  217.  
  218. static gint
  219. start_browser (gchar *prog,
  220.            gchar *url)
  221. {
  222. #ifdef G_OS_WIN32
  223.  
  224.   ShellExecute (HWND_DESKTOP, "open", url, NULL, "C:\\", SW_SHOWNORMAL);
  225.  
  226. #else
  227. #ifndef __EMX__
  228.   pid_t cpid;
  229.  
  230.   if ((cpid = fork()) == 0)
  231.     {
  232.       execlp (prog, prog, url, NULL);
  233.       exit (1);
  234.     }
  235.  
  236.   return (cpid > 0);
  237. #else
  238.   return (spawnlp (P_NOWAIT, prog, prog, url, NULL) != -1);
  239. #endif
  240. #endif /* !G_OS_WIN32 */
  241. }
  242.  
  243. static gint
  244. open_url (gchar *url,
  245.       gint   new_window)
  246. {
  247.   gchar buf[512];
  248.  
  249.   while (isspace (*url))
  250.     ++url;
  251.  
  252. #ifndef G_OS_WIN32
  253.   sprintf (buf, "openURL(%s%s)", url, new_window ? ",new-window" : "");
  254.  
  255.   if (mozilla_remote (buf))
  256.     return (TRUE);
  257. #endif
  258.   return (start_browser (BROWSER_PROGNAME, url));
  259. }
  260.  
  261. static gint
  262. open_url_dialog (void)
  263. {
  264.   GtkWidget *dlg;
  265.   GtkWidget *hbox;
  266.   GtkWidget *button;
  267.   GtkWidget *entry;
  268.   GtkWidget *table;
  269.   GSList    *group;
  270.   gchar      buffer[256];
  271.  
  272.   gimp_ui_init ("webbrowser", FALSE);
  273.  
  274.   dlg = gimp_dialog_new (_("Open URL"), "webbbrowser",
  275.              gimp_standard_help_func, "filters/webbrowser.html",
  276.              GTK_WIN_POS_MOUSE,
  277.              FALSE, TRUE, FALSE,
  278.  
  279.              _("About"), about_callback,
  280.              NULL, NULL, NULL, FALSE, FALSE,
  281.              _("OK"), ok_callback,
  282.              NULL, NULL, NULL, TRUE, FALSE,
  283.              _("Cancel"), gtk_widget_destroy,
  284.              NULL, 1, NULL, FALSE, TRUE,
  285.  
  286.              NULL);
  287.  
  288.   gtk_signal_connect (GTK_OBJECT (dlg), "destroy",
  289.               GTK_SIGNAL_FUNC (gtk_main_quit),
  290.               NULL);
  291.  
  292.   /* table */
  293.   table = gtk_table_new (2, 2, FALSE);
  294.   gtk_table_set_col_spacings (GTK_TABLE (table), 4);
  295.   gtk_table_set_row_spacings (GTK_TABLE (table), 4);
  296.   gtk_container_set_border_width (GTK_CONTAINER (table), 6);
  297.   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), table, FALSE, FALSE, 0);
  298.   gtk_widget_show (table);
  299.  
  300.   /* URL */
  301.   entry = gtk_entry_new ();
  302.   gtk_widget_set_usize (entry, 200, 0);
  303.   g_snprintf (buffer, sizeof (buffer), "%s", url_info.url);
  304.   gtk_entry_set_text (GTK_ENTRY (entry), buffer);
  305.   gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
  306.                  _("URL:"), 1.0, 0.5,
  307.                  entry, 1, FALSE);
  308.   gtk_signal_connect (GTK_OBJECT (entry), "changed",
  309.               GTK_SIGNAL_FUNC (url_callback),
  310.               &url_info.url);
  311.   gtk_widget_show (entry);
  312.  
  313.   /* Window */
  314.   hbox = gtk_hbox_new (FALSE, 4);
  315.   gimp_table_attach_aligned (GTK_TABLE (table), 0, 1,
  316.                  _("Window:"), 1.0, 0.5,
  317.                  hbox, 1, FALSE);
  318.  
  319.   button = gtk_radio_button_new_with_label (NULL, _("New"));
  320.   group = gtk_radio_button_group (GTK_RADIO_BUTTON (button));
  321.   if (url_info.new_window == OPEN_URL_NEW_WINDOW)
  322.     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
  323.   gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
  324.   gtk_signal_connect (GTK_OBJECT (button), "toggled",
  325.               GTK_SIGNAL_FUNC (new_window_callback),
  326.               (gpointer) OPEN_URL_NEW_WINDOW);
  327.   gtk_widget_show (button);
  328.  
  329.   button = gtk_radio_button_new_with_label (group, _("Current"));
  330.   group = gtk_radio_button_group (GTK_RADIO_BUTTON (button));
  331.   if (url_info.new_window == OPEN_URL_CURRENT_WINDOW)
  332.     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
  333.   gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
  334.   gtk_signal_connect (GTK_OBJECT (button), "toggled",
  335.               GTK_SIGNAL_FUNC (new_window_callback),
  336.               (gpointer) OPEN_URL_CURRENT_WINDOW);
  337.   gtk_widget_show (button);
  338.  
  339.   gtk_widget_show (dlg);
  340.  
  341.   gtk_main ();
  342.   gdk_flush ();
  343.  
  344.   return run_flag;
  345. }
  346.  
  347. static void
  348. ok_callback (GtkWidget *widget,
  349.          gpointer   data)
  350. {
  351.   run_flag = TRUE;
  352.  
  353.   gtk_widget_destroy (GTK_WIDGET (data));
  354. }
  355.  
  356. static void
  357. about_callback (GtkWidget *widget,
  358.         gpointer   data)
  359. {
  360.   open_url ("http://www.xcf.berkeley.edu/~misha/gimp/", OPEN_URL_NEW_WINDOW);
  361. }
  362.  
  363. static void
  364. new_window_callback (GtkWidget *widget,
  365.              gpointer   data)
  366. {
  367.   if (GTK_TOGGLE_BUTTON (widget)->active)
  368.     {
  369.       url_info.new_window = (gint) data;
  370.     }
  371. }
  372.  
  373. static void
  374. url_callback (GtkWidget *widget,
  375.           gpointer   data)
  376. {
  377.   strncpy (url_info.url, gtk_entry_get_text (GTK_ENTRY (widget)), URL_BUF_SIZE);
  378. }
  379.  
  380. #ifndef G_OS_WIN32
  381.  
  382. /* -*- Mode:C; tab-width: 8 -*-
  383.  * remote.c --- remote control of Netscape Navigator for Unix.
  384.  * version 1.1.3, for Netscape Navigator 1.1 and newer.
  385.  *
  386.  * Copyright ⌐ 1996 Netscape Communications Corporation, all rights reserved.
  387.  * Created: Jamie Zawinski <jwz@netscape.com>, 24-Dec-94.
  388.  *
  389.  * Permission to use, copy, modify, distribute, and sell this software and its
  390.  * documentation for any purpose is hereby granted without fee, provided that
  391.  * the above copyright notice appear in all copies and that both that
  392.  * copyright notice and this permission notice appear in supporting
  393.  * documentation.  No representations are made about the suitability of this
  394.  * software for any purpose.  It is provided "as is" without express or 
  395.  * implied warranty.
  396.  *
  397.  * To compile:
  398.  *
  399.  *    cc -o netscape-remote remote.c -DSTANDALONE -lXmu -lX11
  400.  *
  401.  * To use:
  402.  *
  403.  *    netscape-remote -help
  404.  *
  405.  * Documentation for the protocol which this code implements may be found at:
  406.  *
  407.  *    http://home.netscape.com/newsref/std/x-remote.html
  408.  *
  409.  * Bugs and commentary to x_cbug@netscape.com.
  410.  */
  411.  
  412.  
  413.  
  414. /* vroot.h is a header file which lets a client get along with `virtual root'
  415.    window managers like swm, tvtwm, olvwm, etc.  If you don't have this header
  416.    file, you can find it at "http://home.netscape.com/newsref/std/vroot.h".
  417.    If you don't care about supporting virtual root window managers, you can
  418.    comment this line out.
  419.  */
  420. /* #include "vroot.h" */
  421. /* Begin vroot.h */
  422. /*****************************************************************************/
  423. /**                   Copyright 1991 by Andreas Stolcke                     **/
  424. /**               Copyright 1990 by Solbourne Computer Inc.                 **/
  425. /**                          Longmont, Colorado                             **/
  426. /**                                                                         **/
  427. /**                           All Rights Reserved                           **/
  428. /**                                                                         **/
  429. /**    Permission to use, copy, modify, and distribute this software and    **/
  430. /**    its documentation  for  any  purpose  and  without  fee is hereby    **/
  431. /**    granted, provided that the above copyright notice appear  in  all    **/
  432. /**    copies and that both  that  copyright  notice  and  this  permis-    **/
  433. /**    sion  notice appear in supporting  documentation,  and  that  the    **/
  434. /**    name of Solbourne not be used in advertising                         **/
  435. /**    in publicity pertaining to distribution of the  software  without    **/
  436. /**    specific, written prior permission.                                  **/
  437. /**                                                                         **/
  438. /**    ANDREAS STOLCKE AND SOLBOURNE COMPUTER INC. DISCLAIMS ALL WARRANTIES **/
  439. /**    WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF    **/
  440. /**    MERCHANTABILITY  AND  FITNESS,  IN  NO  EVENT SHALL ANDREAS STOLCKE  **/
  441. /**    OR SOLBOURNE BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL    **/
  442. /**    DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA   **/
  443. /**    OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER    **/
  444. /**    TORTIOUS ACTION, ARISING OUT OF OR IN  CONNECTION  WITH  THE  USE    **/
  445. /**    OR PERFORMANCE OF THIS SOFTWARE.                                     **/
  446. /*****************************************************************************/
  447. /*
  448.  * vroot.h -- Virtual Root Window handling header file
  449.  *
  450.  * This header file redefines the X11 macros RootWindow and DefaultRootWindow,
  451.  * making them look for a virtual root window as provided by certain `virtual'
  452.  * window managers like swm and tvtwm. If none is found, the ordinary root
  453.  * window is returned, thus retaining backward compatibility with standard
  454.  * window managers.
  455.  * The function implementing the virtual root lookup remembers the result of
  456.  * its last invocation to avoid overhead in the case of repeated calls
  457.  * on the same display and screen arguments. 
  458.  * The lookup code itself is taken from Tom LaStrange's ssetroot program.
  459.  *
  460.  * Most simple root window changing X programs can be converted to using
  461.  * virtual roots by just including
  462.  *
  463.  * #include <X11/vroot.h>
  464.  *
  465.  * after all the X11 header files.  It has been tested on such popular
  466.  * X clients as xphoon, xfroot, xloadimage, and xaqua.
  467.  * It also works with the core clients xprop, xwininfo, xwd, and editres
  468.  * (and is necessary to get those clients working under tvtwm).
  469.  * It does NOT work with xsetroot; get the xsetroot replacement included in
  470.  * the tvtwm distribution instead.
  471.  *
  472.  * Andreas Stolcke <stolcke@ICSI.Berkeley.EDU>, 9/7/90
  473.  * - replaced all NULL's with properly cast 0's, 5/6/91
  474.  * - free children list (suggested by Mark Martin <mmm@cetia.fr>), 5/16/91
  475.  * - include X11/Xlib.h and support RootWindowOfScreen, too 9/17/91
  476.  */
  477.  
  478. #ifndef _VROOT_H_
  479. #define _VROOT_H_
  480.  
  481.  
  482. static Window
  483. VirtualRootWindowOfScreen(screen)
  484.     Screen *screen;
  485. {
  486.     static Screen *save_screen = (Screen *)0;
  487.     static Window root = (Window)0;
  488.  
  489.     if (screen != save_screen) {
  490.         Display *dpy = DisplayOfScreen(screen);
  491.         Atom __SWM_VROOT = None;
  492.         int i;
  493.         Window rootReturn, parentReturn, *children;
  494.         unsigned int numChildren;
  495.  
  496.         root = RootWindowOfScreen(screen);
  497.  
  498.         /* go look for a virtual root */
  499.         __SWM_VROOT = XInternAtom(dpy, "__SWM_VROOT", False);
  500.         if (XQueryTree(dpy, root, &rootReturn, &parentReturn,
  501.                  &children, &numChildren)) {
  502.             for (i = 0; i < numChildren; i++) {
  503.                 Atom actual_type;
  504.                 int actual_format;
  505.                 unsigned long nitems, bytesafter;
  506.                 Window *newRoot = (Window *)0;
  507.  
  508.                 if (XGetWindowProperty(dpy, children[i],
  509.                     __SWM_VROOT, 0, 1, False, XA_WINDOW,
  510.                     &actual_type, &actual_format,
  511.                     &nitems, &bytesafter,
  512.                     (unsigned char **) &newRoot) == Success
  513.                     && newRoot) {
  514.                     root = *newRoot;
  515.                     break;
  516.                 }
  517.             }
  518.             if (children)
  519.                 XFree((char *)children);
  520.         }
  521.  
  522.         save_screen = screen;
  523.     }
  524.  
  525.     return root;
  526. }
  527.  
  528. #undef RootWindowOfScreen
  529. #define RootWindowOfScreen(s) VirtualRootWindowOfScreen(s)
  530.  
  531. #undef RootWindow
  532. #define RootWindow(dpy,screen) VirtualRootWindowOfScreen(ScreenOfDisplay(dpy,screen))
  533.  
  534. #undef DefaultRootWindow
  535. #define DefaultRootWindow(dpy) VirtualRootWindowOfScreen(DefaultScreenOfDisplay(dpy))
  536.  
  537. #endif /* _VROOT_H_ */
  538. /* End vroot.h */
  539.  
  540.  
  541. #ifdef DIAGNOSTIC
  542. #ifdef STANDALONE
  543.  static const char *progname = 0;
  544.  static const char *expected_mozilla_version = "1.1";
  545. #else  /* !STANDALONE */
  546.  extern const char *progname;
  547.  extern const char *expected_mozilla_version;
  548. #endif /* !STANDALONE */
  549. #endif /* DIAGNOSTIC */
  550.  
  551. #define MOZILLA_VERSION_PROP   "_MOZILLA_VERSION"
  552. #define MOZILLA_LOCK_PROP      "_MOZILLA_LOCK"
  553. #define MOZILLA_COMMAND_PROP   "_MOZILLA_COMMAND"
  554. #define MOZILLA_RESPONSE_PROP  "_MOZILLA_RESPONSE"
  555. static Atom XA_MOZILLA_VERSION  = 0;
  556. static Atom XA_MOZILLA_LOCK     = 0;
  557. static Atom XA_MOZILLA_COMMAND  = 0;
  558. static Atom XA_MOZILLA_RESPONSE = 0;
  559.  
  560. static void
  561. mozilla_remote_init_atoms (Display *dpy)
  562. {
  563.   if (! XA_MOZILLA_VERSION)
  564.     XA_MOZILLA_VERSION = XInternAtom (dpy, MOZILLA_VERSION_PROP, False);
  565.   if (! XA_MOZILLA_LOCK)
  566.     XA_MOZILLA_LOCK = XInternAtom (dpy, MOZILLA_LOCK_PROP, False);
  567.   if (! XA_MOZILLA_COMMAND)
  568.     XA_MOZILLA_COMMAND = XInternAtom (dpy, MOZILLA_COMMAND_PROP, False);
  569.   if (! XA_MOZILLA_RESPONSE)
  570.     XA_MOZILLA_RESPONSE = XInternAtom (dpy, MOZILLA_RESPONSE_PROP, False);
  571. }
  572.  
  573. static Window
  574. mozilla_remote_find_window (Display *dpy)
  575. {
  576.   int i;
  577.   Window root = RootWindowOfScreen (DefaultScreenOfDisplay (dpy));
  578.   Window root2, parent, *kids;
  579.   unsigned int nkids;
  580.   Window result = 0;
  581.  
  582.   if (! XQueryTree (dpy, root, &root2, &parent, &kids, &nkids))
  583.     {
  584. #ifdef DIAGNOSTIC
  585.       fprintf (stderr, "%s: XQueryTree failed on display %s\n", progname,
  586.            DisplayString (dpy));
  587. #endif
  588.       return (0);
  589.     }
  590.  
  591.   /* root != root2 is possible with virtual root WMs. */
  592.  
  593.   if (! (kids && nkids))
  594.     {
  595. #ifdef DIAGNOSTIC
  596.       fprintf (stderr, "%s: root window has no children on display %s\n",
  597.            progname, DisplayString (dpy));
  598. #endif
  599.       return (0);
  600.     }
  601.  
  602.   for (i = nkids-1; i >= 0; i--)
  603.     {
  604.       Atom type;
  605.       int format;
  606.       unsigned long nitems, bytesafter;
  607.       unsigned char *version = 0;
  608.       Window w = XmuClientWindow (dpy, kids[i]);
  609.       int status = XGetWindowProperty (dpy, w, XA_MOZILLA_VERSION,
  610.                        0, (65536 / sizeof (long)),
  611.                        False, XA_STRING,
  612.                        &type, &format, &nitems, &bytesafter,
  613.                        &version);
  614.       if (! version)
  615.     continue;
  616.  
  617.       XFree (version);
  618.       if (status == Success && type != None)
  619.     {
  620.       result = w;
  621.       break;
  622.     }
  623.     }
  624.  
  625.   return result;
  626. }
  627.  
  628. static char *lock_data = 0;
  629.  
  630. static int
  631. mozilla_remote_obtain_lock (Display *dpy, Window window)
  632. {
  633.   Bool locked = False;
  634.   Bool waited = False;
  635.  
  636.   if (! lock_data)
  637.     {
  638.       lock_data = (char *) malloc (255);
  639.       sprintf (lock_data, "pid%d@", getpid ());
  640.       if (gethostname (lock_data + strlen (lock_data), 100))
  641.     {
  642.       return (-1);
  643.     }
  644.     }
  645.  
  646.   do
  647.     {
  648.       int result;
  649.       Atom actual_type;
  650.       int actual_format;
  651.       unsigned long nitems, bytes_after;
  652.       unsigned char *data = 0;
  653.  
  654.       XGrabServer (dpy);   /* ################################# DANGER! */
  655.  
  656.       result = XGetWindowProperty (dpy, window, XA_MOZILLA_LOCK,
  657.                    0, (65536 / sizeof (long)),
  658.                    False, /* don't delete */
  659.                    XA_STRING,
  660.                    &actual_type, &actual_format,
  661.                    &nitems, &bytes_after,
  662.                    &data);
  663.       if (result != Success || actual_type == None)
  664.     {
  665.       /* It's not now locked - lock it. */
  666. #ifdef DEBUG_PROPS
  667.       fprintf (stderr, "%s: (writing " MOZILLA_LOCK_PROP
  668.            " \"%s\" to 0x%x)\n",
  669.            progname, lock_data, (unsigned int) window);
  670. #endif
  671.       XChangeProperty (dpy, window, XA_MOZILLA_LOCK, XA_STRING, 8,
  672.                PropModeReplace, (unsigned char *) lock_data,
  673.                strlen (lock_data));
  674.       locked = True;
  675.     }
  676.  
  677.       XUngrabServer (dpy); /* ################################# danger over */
  678.       XSync (dpy, False);
  679.  
  680.       if (! locked)
  681.     {
  682.       /* We tried to grab the lock this time, and failed because someone
  683.          else is holding it already.  So, wait for a PropertyDelete event
  684.          to come in, and try again. */
  685.  
  686. #ifdef DIAGNOSTIC
  687.       fprintf (stderr, "%s: window 0x%x is locked by %s; waiting...\n",
  688.            progname, (unsigned int) window, data);
  689. #endif
  690.       waited = True;
  691.  
  692.       while (1)
  693.         {
  694.           XEvent event;
  695.           XNextEvent (dpy, &event);
  696.           if (event.xany.type == DestroyNotify &&
  697.           event.xdestroywindow.window == window)
  698.         {
  699. #ifdef DIAGNOSTIC
  700.           fprintf (stderr, "%s: window 0x%x unexpectedly destroyed.\n",
  701.                progname, (unsigned int) window);
  702. #endif
  703.           return (6);
  704.         }
  705.           else if (event.xany.type == PropertyNotify &&
  706.                event.xproperty.state == PropertyDelete &&
  707.                event.xproperty.window == window &&
  708.                event.xproperty.atom == XA_MOZILLA_LOCK)
  709.         {
  710.           /* Ok!  Someone deleted their lock, so now we can try
  711.              again. */
  712. #ifdef DEBUG_PROPS
  713.           fprintf (stderr, "%s: (0x%x unlocked, trying again...)\n",
  714.                progname, (unsigned int) window);
  715. #endif
  716.           break;
  717.         }
  718.         }
  719.     }
  720.       if (data)
  721.     XFree (data);
  722.     }
  723.   while (! locked);
  724.  
  725. #ifdef DIAGNOSTIC
  726.   if (waited)
  727.     fprintf (stderr, "%s: obtained lock.\n", progname);
  728. #endif
  729.   return (0);
  730. }
  731.  
  732.  
  733. static void
  734. mozilla_remote_free_lock (Display *dpy, Window window)
  735. {
  736.   int result;
  737.   Atom actual_type;
  738.   int actual_format;
  739.   unsigned long nitems, bytes_after;
  740.   unsigned char *data = 0;
  741.  
  742. #ifdef DEBUG_PROPS
  743.       fprintf (stderr, "%s: (deleting " MOZILLA_LOCK_PROP
  744.            " \"%s\" from 0x%x)\n",
  745.            progname, lock_data, (unsigned int) window);
  746. #endif
  747.  
  748.   result = XGetWindowProperty (dpy, window, XA_MOZILLA_LOCK,
  749.                    0, (65536 / sizeof (long)),
  750.                    True, /* atomic delete after */
  751.                    XA_STRING,
  752.                    &actual_type, &actual_format,
  753.                    &nitems, &bytes_after,
  754.                    &data);
  755.   if (result != Success)
  756.     {
  757. #ifdef DIAGNOSTIC
  758.       fprintf (stderr, "%s: unable to read and delete " MOZILLA_LOCK_PROP
  759.            " property\n",
  760.            progname);
  761. #endif
  762.       return;
  763.     }
  764.   else if (!data || !*data)
  765.     {
  766. #ifdef DIAGNOSTIC
  767.       fprintf (stderr, "%s: invalid data on " MOZILLA_LOCK_PROP
  768.            " of window 0x%x.\n",
  769.            progname, (unsigned int) window);
  770. #endif
  771.       return;
  772.     }
  773.   else if (strcmp ((char *) data, lock_data))
  774.     {
  775. #ifdef DIAGNOSTIC
  776.       fprintf (stderr, "%s: " MOZILLA_LOCK_PROP
  777.            " was stolen!  Expected \"%s\", saw \"%s\"!\n",
  778.            progname, lock_data, data);
  779. #endif
  780.       return;
  781.     }
  782.  
  783.   if (data)
  784.     XFree (data);
  785. }
  786.  
  787.  
  788. static int
  789. mozilla_remote_command (Display *dpy, Window window, const char *command)
  790. {
  791.   int result = 0;
  792.   Bool done = False;
  793.  
  794. #ifdef DEBUG_PROPS
  795.   fprintf (stderr, "%s: (writing " MOZILLA_COMMAND_PROP " \"%s\" to 0x%x)\n",
  796.        progname, command, (unsigned int) window);
  797. #endif
  798.  
  799.   XChangeProperty (dpy, window, XA_MOZILLA_COMMAND, XA_STRING, 8,
  800.            PropModeReplace, (unsigned char *) command,
  801.            strlen (command));
  802.  
  803.   while (!done)
  804.     {
  805.       XEvent event;
  806.       XNextEvent (dpy, &event);
  807.       if (event.xany.type == DestroyNotify &&
  808.       event.xdestroywindow.window == window)
  809.     {
  810.       /* Print to warn user...*/
  811. #ifdef DIAGNOSTIC
  812.       fprintf (stderr, "%s: window 0x%x was destroyed.\n",
  813.            progname, (unsigned int) window);
  814. #endif
  815.       result = 6;
  816.       goto DONE;
  817.     }
  818.       else if (event.xany.type == PropertyNotify &&
  819.            event.xproperty.state == PropertyNewValue &&
  820.            event.xproperty.window == window &&
  821.            event.xproperty.atom == XA_MOZILLA_RESPONSE)
  822.     {
  823.       Atom actual_type;
  824.       int actual_format;
  825.       unsigned long nitems, bytes_after;
  826.       unsigned char *data = 0;
  827.  
  828.       result = XGetWindowProperty (dpy, window, XA_MOZILLA_RESPONSE,
  829.                        0, (65536 / sizeof (long)),
  830.                        True, /* atomic delete after */
  831.                        XA_STRING,
  832.                        &actual_type, &actual_format,
  833.                        &nitems, &bytes_after,
  834.                        &data);
  835. #ifdef DEBUG_PROPS
  836.       if (result == Success && data && *data)
  837.         {
  838.           fprintf (stderr, "%s: (server sent " MOZILLA_RESPONSE_PROP
  839.                " \"%s\" to 0x%x.)\n",
  840.                progname, data, (unsigned int) window);
  841.         }
  842. #endif
  843.  
  844.       if (result != Success)
  845.         {
  846. #ifdef DIAGNOSTIC
  847.           fprintf (stderr, "%s: failed reading " MOZILLA_RESPONSE_PROP
  848.                " from window 0x%0x.\n",
  849.                progname, (unsigned int) window);
  850. #endif
  851.           result = 6;
  852.           done = True;
  853.         }
  854.       else if (!data || strlen((char *) data) < 5)
  855.         {
  856. #ifdef DIAGNOSTIC
  857.           fprintf (stderr, "%s: invalid data on " MOZILLA_RESPONSE_PROP
  858.                " property of window 0x%0x.\n",
  859.                progname, (unsigned int) window);
  860. #endif
  861.           result = 6;
  862.           done = True;
  863.         }
  864.       else if (*data == '1')    /* positive preliminary reply */
  865.         {
  866. #ifdef DIAGNOSTIC
  867.           fprintf (stderr, "%s: %s\n", progname, data + 4);
  868. #endif
  869.           /* keep going */
  870.           done = False;
  871.         }
  872. #if 1
  873.       else if (!strncmp ((char *)data, "200", 3)) /* positive completion */
  874.         {
  875.           result = 0;
  876.           done = True;
  877.         }
  878. #endif
  879.       else if (*data == '2')        /* positive completion */
  880.         {
  881. #ifdef DIAGNOSTIC
  882.           fprintf (stderr, "%s: %s\n", progname, data + 4);
  883. #endif
  884.           result = 0;
  885.           done = True;
  886.         }
  887.       else if (*data == '3')    /* positive intermediate reply */
  888.         {
  889. #ifdef DIAGNOSTIC
  890.           fprintf (stderr, "%s: internal error: "
  891.                "server wants more information?  (%s)\n",
  892.                progname, data);
  893. #endif
  894.           result = 3;
  895.           done = True;
  896.         }
  897.       else if (*data == '4' ||    /* transient negative completion */
  898.            *data == '5')    /* permanent negative completion */
  899.         {
  900. #ifdef DIAGNOSTIC
  901.           fprintf (stderr, "%s: %s\n", progname, data + 4);
  902. #endif
  903.           result = (*data - '0');
  904.           done = True;
  905.         }
  906.       else
  907.         {
  908. #ifdef DIAGNOSTIC
  909.           fprintf (stderr,
  910.                "%s: unrecognised " MOZILLA_RESPONSE_PROP
  911.                " from window 0x%x: %s\n",
  912.                progname, (unsigned int) window, data);
  913. #endif
  914.           result = 6;
  915.           done = True;
  916.         }
  917.  
  918.       if (data)
  919.         XFree (data);
  920.     }
  921. #ifdef DEBUG_PROPS
  922.       else if (event.xany.type == PropertyNotify &&
  923.            event.xproperty.window == window &&
  924.            event.xproperty.state == PropertyDelete &&
  925.            event.xproperty.atom == XA_MOZILLA_COMMAND)
  926.     {
  927.       fprintf (stderr, "%s: (server 0x%x has accepted "
  928.            MOZILLA_COMMAND_PROP ".)\n",
  929.            progname, (unsigned int) window);
  930.     }
  931. #endif /* DEBUG_PROPS */
  932.     }
  933.  
  934.  DONE:
  935.  
  936.   return result;
  937. }
  938.  
  939. static int
  940. mozilla_remote (char *command)
  941. {
  942.   int status = 0;
  943.   Display *dpy;
  944.   Window window;
  945.  
  946.   if (!(dpy = XOpenDisplay (NULL)))
  947.     return (0);
  948.  
  949.   mozilla_remote_init_atoms (dpy);
  950.  
  951.   if (!(window = mozilla_remote_find_window (dpy)))
  952.     return (0);
  953.  
  954.   XSelectInput (dpy, window, (PropertyChangeMask|StructureNotifyMask));
  955.  
  956.   if (mozilla_remote_obtain_lock (dpy, window))
  957.     return (0);
  958.  
  959.   status = mozilla_remote_command (dpy, window, command);
  960.  
  961.   /* When status = 6, it means the window has been destroyed */
  962.   /* It is invalid to free the lock when window is destroyed. */
  963.  
  964.   if ( status != 6 )
  965.     mozilla_remote_free_lock (dpy, window);
  966.  
  967.   return (!status);
  968. }
  969.  
  970. #endif /* !G_OS_WIN32 */
  971.