1 /* multihead.c -- Simple multihead support
2    $Id$
3 
4    Copyright (C) 2001 John Harper <jsh@pixelslut.com>
5 
6    This file is part of sawfish.
7 
8    sawfish is free software; you can redistribute it and/or modify it
9    under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2, or (at your option)
11    any later version.
12 
13    sawfish is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17 
18    You should have received a copy of the GNU General Public License
19    along with sawfish; see the file COPYING.   If not, write to
20    the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
21 
22 /* AIX requires this to be the first thing in the file.  */
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26 #ifndef __GNUC__
27 # if HAVE_ALLOCA_H
28 #  include <alloca.h>
29 # else
30 #  ifdef _AIX
31  #pragma alloca
32 #  else
33 #   ifndef alloca /* predefined by HP cc +Olibcalls */
34    char *alloca ();
35 #   endif
36 #  endif
37 # endif
38 #endif
39 
40 #include "sawfish.h"
41 
42 #include <string.h>
43 #include <assert.h>
44 #ifdef HAVE_UNISTD_H
45 # include <unistd.h>
46 #endif
47 
48 static inline bool
argv_test_option(const char * option,int argc,char ** argv,int * positionp,char ** value)49 argv_test_option (const char *option, int argc, char **argv,
50 		  int *positionp, char **value)
51 {
52     int optlen = strlen (option);
53     int position = (*positionp)++;
54 
55     if (strncmp (option, argv[position], optlen) != 0)
56 	return FALSE;
57 
58     if (value != 0)
59     {
60 	/* Argument required */
61 
62 	if (argv[position][optlen] == '=')
63 	    *value = argv[position] + optlen + 1;
64 	else if (argv[position][optlen] == 0 && position + 1 < argc)
65 	{
66 	    *value = argv[position + 1];
67 	    (*positionp)++;
68 	}
69 	else
70 	    return FALSE;
71     }
72 
73     return TRUE;
74 }
75 
76 static bool
argv_get_option(int argc,char ** argv,const char * option,char ** valuep)77 argv_get_option (int argc, char **argv, const char *option, char **valuep)
78 {
79     int i = 1;
80     while (i < argc)
81     {
82 	if (argv_test_option (option, argc, argv, &i, valuep))
83 	    return TRUE;
84     }
85     return FALSE;
86 }
87 
88 static char **
copy_argv(int argc,char ** argv)89 copy_argv (int argc, char **argv)
90 {
91     char **copy = malloc (sizeof (char *) * argc);
92     if (copy == 0)
93 	abort ();
94     memcpy (copy, argv, sizeof (char *) * argc);
95     return copy;
96 }
97 
98 static void
grow_argv(int * argcp,char *** argvp,int extra_slots)99 grow_argv (int *argcp, char ***argvp, int extra_slots)
100 {
101     if (extra_slots > 0)
102     {
103 	char **copy = malloc (sizeof (char *) * (*argcp + extra_slots));
104 	if (copy == 0)
105 	    abort ();
106 	memcpy (copy, *argvp, sizeof (char *) * *argcp);
107 	free (*argvp);
108 	*argvp = copy;
109 	*argcp += extra_slots;
110     }
111 }
112 
113 static void
argv_remove_option(int * argcp,char *** argvp,char * option,bool has_arg)114 argv_remove_option (int *argcp, char ***argvp, char *option, bool has_arg)
115 {
116     int i = 1;
117 
118     while (i < *argcp)
119     {
120 	char *value;
121 	int old_i = i;
122 
123 	if (argv_test_option (option, *argcp, *argvp, &i, has_arg ? &value : 0))
124 	{
125 	    if (!has_arg || i - old_i == 1)
126 	    {
127 		/* Option only takes one slot, i.e. --foo=bar form.
128 		So just delete the single slot */
129 		memmove (*argvp + old_i, *argvp + old_i + 1,
130 			 sizeof (char *) * (*argcp - (old_i + 1)));
131 		*argcp -= 1;
132 	    }
133 	    else
134 	    {
135 		memmove (*argvp + old_i, *argvp + old_i + 2,
136 			 sizeof (char *) * (*argcp - (old_i + 2)));
137 		*argcp -= 2;
138 	    }
139 
140 	    return;
141 	}
142     }
143 }
144 
145 static void
argv_set_option(int * argcp,char *** argvp,char * option,char * arg)146 argv_set_option (int *argcp, char ***argvp, char *option, char *arg)
147 {
148     int i = 1;
149 
150     while (i < *argcp)
151     {
152 	char *value;
153 	int old_i = i;
154 
155 	if (argv_test_option (option, *argcp, *argvp, &i, arg ? &value : 0))
156 	{
157 	    if (arg != 0)
158 	    {
159 		if (i - old_i == 1)
160 		{
161 		    /* Option only takes one slot, i.e. --foo=bar form.
162 		       So open up space for the second slot */
163 		    grow_argv (argcp, argvp, 1);
164 		    memmove (*argvp + i + 1, *argvp + i,
165 			     sizeof (char *) * (*argcp - (i + 1)));
166 		    (*argvp)[old_i] = option;
167 		    i++;
168 		}
169 
170 		(*argvp)[old_i + 1] = arg;
171 	    }
172 
173 	    return;
174 	}
175     }
176 
177     /* No existing instance of this option. */
178     if (arg != 0)
179     {
180 	grow_argv (argcp, argvp, 2);
181 	(*argvp)[*argcp - 2] = option;
182 	(*argvp)[*argcp - 1] = arg;
183     }
184     else
185     {
186 	grow_argv (argcp, argvp, 1);
187 	(*argvp)[*argcp - 1] = option;
188     }
189 }
190 
191 /* Spawn multiple copies, one per screen, if necessary */
192 void
multihead_init(int * argcp,char *** argvp)193 multihead_init (int *argcp, char ***argvp)
194 {
195     int argc = *argcp;
196     char **argv = *argvp;
197     char *dpy_name, *dpy_copy, *tem;
198     Display *dpy;
199     int i, master_screen, total_screens;
200 
201     if (!argv_get_option (argc, argv, "--multihead", NULL))
202 	return;
203 
204     fputs ("\n\
205 Warning: sawfish's --multihead option is known to have fundamental\n\
206          design flaws, which may lead to sawfish behaving strangely, or\n\
207          the files in your ~/.sawfish directory being corrupted. Use\n\
208          this option at your own risk!\n\n", stderr);
209 
210     if (!argv_get_option (argc, argv, "--display", &dpy_name))
211 	dpy_name = getenv ("DISPLAY");
212 
213     dpy = XOpenDisplay (dpy_name);
214     if (dpy == 0)
215 	return;
216 
217     argv = copy_argv (argc, argv);
218 
219     dpy_name = XDisplayString (dpy);
220     dpy_copy = strdup (dpy_name);
221     tem = strrchr (dpy_copy, '.');
222     assert (tem != 0);
223     tem[1] = 0;
224 
225     master_screen = DefaultScreen (dpy);
226     total_screens = ScreenCount (dpy);
227 
228     /* For the master */
229     argv_set_option (&argc, &argv, "--display", strdup (dpy_name));
230 
231     /* This will remove it from all future children as well.. */
232     argv_remove_option (&argc, &argv, "--multihead", FALSE);
233 
234     XCloseDisplay (dpy);
235 
236     for (i = 0; i < total_screens; i++)
237     {
238 	if (i != master_screen && fork () == 0)
239 	{
240 	    dpy_name = malloc (strlen (dpy_copy) + 6);
241 	    sprintf (dpy_name, "%s%d", dpy_copy, i);
242 
243 	    /* Give each instance the correct display name */
244 	    argv_set_option (&argc, &argv, "--display", dpy_name);
245 
246 	    break;
247 	}
248     }
249 
250     *argcp = argc;
251     *argvp = argv;
252 }
253