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