A tiling window manager
1/*
2 * Copyright (C) 2000, 2001, 2002, 2003, 2004 Shawn Betts <sabetts@vcn.bc.ca>
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the Free
6 * Software Foundation; either version 2 of the License, or (at your option)
7 * any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along with
15 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
16 * Place, Suite 330, Boston, MA 02111-1307 USA.
17 */
18
19#include <X11/X.h>
20#include <X11/Xlib.h>
21#include <X11/Xutil.h>
22#include <X11/Xatom.h>
23#include <X11/Xproto.h>
24#include <X11/cursorfont.h>
25#include <sys/stat.h>
26
27#include <err.h>
28#include <errno.h>
29#include <stdio.h>
30#include <stdlib.h>
31#include <signal.h>
32#include <unistd.h>
33#include <getopt.h>
34#include <string.h>
35#include <sys/wait.h>
36#include <ctype.h>
37
38#include "sdorfehs.h"
39
40static void
41sighandler(int signum)
42{
43 kill_signalled++;
44}
45
46static void
47hup_handler(int signum)
48{
49 hup_signalled++;
50}
51
52static void
53alrm_handler(int signum)
54{
55 alarm_signalled++;
56}
57
58static int
59handler(Display *d, XErrorEvent *e)
60{
61 char error_msg[100];
62
63 if (e->request_code == X_ChangeWindowAttributes &&
64 e->error_code == BadAccess)
65 errx(1, "another window manager is already running");
66
67#ifdef IGNORE_BADWINDOW
68 return 0;
69#else
70 if (ignore_badwindow && e->error_code == BadWindow)
71 return 0;
72#endif
73
74 XGetErrorText(d, e->error_code, error_msg, sizeof(error_msg));
75 warnx("X error: %s", error_msg);
76
77 /*
78 * If there is already an error to report, replace it with this new one.
79 */
80 free(rp_error_msg);
81 rp_error_msg = xstrdup(error_msg);
82
83 return 0;
84}
85
86static void
87print_help(void)
88{
89 printf("%s %s\n", PROGNAME, VERSION);
90 printf("usage: %s [-h]\n", PROGNAME);
91 printf(" %s [-d dpy] [-c cmd] [-i] [-f file]\n", PROGNAME);
92 exit(0);
93}
94
95static int
96read_startup_files(const char *alt_rcfile)
97{
98 FILE *fileptr = NULL;
99 char *config_dir, *filename;
100
101 if (alt_rcfile)
102 filename = strdup(alt_rcfile);
103 else {
104 config_dir = get_config_dir();
105 filename = xsprintf("%s/config", config_dir);
106 free(config_dir);
107 }
108
109 fileptr = fopen(filename, "r");
110 if (fileptr == NULL && errno != ENOENT)
111 warn("could not open %s", filename);
112 free(filename);
113
114 if (fileptr != NULL) {
115 set_close_on_exec(fileno(fileptr));
116 read_rc_file(fileptr);
117 fclose(fileptr);
118 return 1;
119 }
120
121 return 0;
122}
123
124/*
125 * Odd that we spend so much code on making sure the silly welcome message is
126 * correct. Oh well...
127 */
128static void
129show_welcome_message(void)
130{
131 rp_action *help_action;
132 char *prefix, *help;
133 rp_keymap *map;
134
135 prefix = keysym_to_string(prefix_key.sym, prefix_key.state);
136
137 map = find_keymap(ROOT_KEYMAP);
138
139 /* Find the help key binding. */
140 help_action = find_keybinding_by_action("help " ROOT_KEYMAP, map);
141 if (help_action)
142 help = keysym_to_string(help_action->key, help_action->state);
143 else
144 help = NULL;
145
146
147 if (help) {
148 /*
149 * A little kludge to use ? instead of `question' for the help key.
150 */
151 if (!strcmp(help, "question"))
152 marked_message_printf(0, 0, MESSAGE_WELCOME, prefix, "?");
153 else
154 marked_message_printf(0, 0, MESSAGE_WELCOME, prefix, help);
155
156 free(help);
157 } else {
158 marked_message_printf(0, 0, MESSAGE_WELCOME, prefix, ":help");
159 }
160
161 free(prefix);
162}
163
164static void
165init_defaults(void)
166{
167 unsigned long atom = 0;
168
169 defaults.top_kmap = xstrdup(TOP_KEYMAP);
170
171 defaults.win_gravity = NorthWestGravity;
172 defaults.trans_gravity = CenterGravity;
173 defaults.maxsize_gravity = CenterGravity;
174
175 defaults.input_window_size = 200;
176 defaults.window_border_width = 1;
177 defaults.only_border = 1;
178 defaults.bar_x_padding = 14;
179 defaults.bar_y_padding = 10;
180 defaults.bar_location = NorthWestGravity;
181 defaults.bar_timeout = 3;
182 defaults.bar_border_width = 0;
183 defaults.bar_in_padding = 1;
184 defaults.bar_sticky = 1;
185
186 defaults.frame_indicator_timeout = 1;
187 defaults.frame_resize_unit = 10;
188
189 defaults.padding_left = 20;
190 defaults.padding_right = 20;
191 defaults.padding_top = 20;
192 defaults.padding_bottom = 20;
193
194 defaults.gap = 10;
195
196 defaults.font_string = xstrdup(DEFAULT_XFT_FONT);
197
198 defaults.fgcolor_string = xstrdup("#eeeeee");
199 defaults.bgcolor_string = xstrdup("black");
200 defaults.fwcolor_string = xstrdup("black");
201 defaults.bwcolor_string = xstrdup("black");
202 defaults.barbordercolor_string = xstrdup("#eeeeee");
203
204 defaults.wait_for_key_cursor = 1;
205
206 defaults.window_fmt = xstrdup("%n%s%t");
207 defaults.info_fmt = xstrdup("(%H, %W) %n(%t)");
208 defaults.frame_fmt = xstrdup("Frame %f (%Wx%H)");
209 defaults.sticky_fmt = xstrdup("%t");
210 defaults.resize_fmt = xstrdup("Resize frame (%Wx%H)");
211
212 defaults.win_name = WIN_NAME_TITLE;
213 defaults.startup_message = 1;
214 defaults.warp = 0;
215 defaults.window_list_style = STYLE_COLUMN;
216
217 defaults.history_size = 20;
218 defaults.frame_selectors = xstrdup("");
219 defaults.maxundos = 20;
220
221 get_atom(DefaultRootWindow(dpy), _net_number_of_desktops, XA_CARDINAL,
222 0, &atom, 1, NULL);
223 if (atom > 0)
224 defaults.vscreens = (int)atom;
225 else
226 defaults.vscreens = 12;
227
228 defaults.ignore_resize_hints = 0;
229 defaults.win_add_cur_vscreen = 0;
230}
231
232int
233main(int argc, char *argv[])
234{
235 int c, fd;
236 char **cmd = NULL;
237 int cmd_count = 0;
238 char *display = NULL;
239 int interactive = 0;
240 char *alt_rcfile = NULL;
241 char pid[8];
242
243 setlocale(LC_CTYPE, "");
244
245 if (XSupportsLocale()) {
246 if (!XSetLocaleModifiers(""))
247 warnx("couldn't set X locale modifiers");
248 } else
249 warnx("X doesn't seem to support your locale");
250
251 /* Parse the arguments */
252 myargv = argv;
253 while ((c = getopt(argc, argv, "c:d:hif:")) != -1) {
254 switch (c) {
255 case 'c':
256 cmd = xrealloc(cmd, sizeof(char *) * (cmd_count + 1));
257 cmd[cmd_count++] = xstrdup(optarg);
258 break;
259 case 'd':
260 display = optarg;
261 break;
262 case 'f':
263 alt_rcfile = optarg;
264 break;
265 case 'h':
266 print_help();
267 break;
268 case 'i':
269 interactive = 1;
270 break;
271 default:
272 warnx("unsupported option %c", c);
273 print_help();
274 }
275 }
276
277 /* Report extra unparsed arguments. */
278 if (optind < argc) {
279 warnx("bogus arguments:");
280 while (optind < argc)
281 fprintf(stderr, "%s ", argv[optind++]);
282 fputc('\n', stderr);
283 exit(1);
284 }
285 if (!(dpy = XOpenDisplay(display)))
286 errx(1, "can't open display %s", display);
287 set_close_on_exec(ConnectionNumber(dpy));
288
289 /* forked commands should not get X console tty as their stdin */
290 fd = open("/dev/null", O_RDONLY);
291 dup2(fd, STDIN_FILENO);
292 close(fd);
293
294 /* Set our own specific Atoms. */
295 rp_selection = XInternAtom(dpy, "RP_SELECTION", False);
296
297 /* TEXT atoms */
298 xa_string = XA_STRING;
299 xa_compound_text = XInternAtom(dpy, "COMPOUND_TEXT", False);
300 xa_utf8_string = XInternAtom(dpy, "UTF8_STRING", False);
301
302 init_control_socket_path();
303
304 if (cmd_count > 0) {
305 int j, exit_status = 0;
306
307 for (j = 0; j < cmd_count; j++) {
308 if (!send_command(interactive, cmd[j]))
309 exit_status = 1;
310 free(cmd[j]);
311 }
312
313 free(cmd);
314 XCloseDisplay(dpy);
315 return exit_status;
316 }
317
318 /* For child processes to know */
319 snprintf(pid, sizeof(pid), "%d", getpid());
320 setenv("SDORFEHS_PID", pid, 1);
321
322 /* Must be first */
323 register_atom(&_net_supported, "_NET_SUPPORTED");
324
325 register_atom(&wm_change_state, "WM_CHANGE_STATE");
326 register_atom(&wm_colormaps, "WM_COLORMAP_WINDOWS");
327 register_atom(&wm_delete, "WM_DELETE_WINDOW");
328 register_atom(&wm_name, "WM_NAME");
329 register_atom(&wm_protocols, "WM_PROTOCOLS");
330 register_atom(&wm_state, "WM_STATE");
331 register_atom(&wm_take_focus, "WM_TAKE_FOCUS");
332
333 register_atom(&_net_active_window, "_NET_ACTIVE_WINDOW");
334 register_atom(&_net_client_list, "_NET_CLIENT_LIST");
335 register_atom(&_net_client_list_stacking, "_NET_CLIENT_LIST_STACKING");
336 register_atom(&_net_current_desktop, "_NET_CURRENT_DESKTOP");
337 register_atom(&_net_number_of_desktops, "_NET_NUMBER_OF_DESKTOPS");
338 register_atom(&_net_workarea, "_NET_WORKAREA");
339
340 register_atom(&_net_wm_name, "_NET_WM_NAME");
341 register_atom(&_net_wm_pid, "_NET_WM_PID");
342 register_atom(&_net_wm_state, "_NET_WM_STATE");
343 register_atom(&_net_wm_state_fullscreen, "_NET_WM_STATE_FULLSCREEN");
344 register_atom(&_net_wm_window_type, "_NET_WM_WINDOW_TYPE");
345 register_atom(&_net_wm_window_type_dialog, "_NET_WM_WINDOW_TYPE_DIALOG");
346 register_atom(&_net_wm_window_type_dock, "_NET_WM_WINDOW_TYPE_DOCK");
347 register_atom(&_net_wm_window_type_splash, "_NET_WM_WINDOW_TYPE_SPLASH");
348 register_atom(&_net_wm_window_type_tooltip,
349 "_NET_WM_WINDOW_TYPE_TOOLTIP");
350 register_atom(&_net_wm_window_type_utility,
351 "_NET_WM_WINDOW_TYPE_UTILITY");
352 register_atom(&_net_supporting_wm_check,
353 "_NET_SUPPORTING_WM_CHECK");
354
355 /* Setup signal handlers. */
356 XSetErrorHandler(handler);
357 set_sig_handler(SIGALRM, alrm_handler);
358 set_sig_handler(SIGTERM, sighandler);
359 set_sig_handler(SIGINT, sighandler);
360 set_sig_handler(SIGHUP, hup_handler);
361 set_sig_handler(SIGCHLD, chld_handler);
362
363 if (bar_mkfifo() == -1)
364 return 1;
365
366 /* Setup our internal structures */
367 init_defaults();
368 init_window_stuff();
369 init_xrandr();
370 init_screens();
371
372 update_modifier_map();
373 init_user_commands();
374 initialize_default_keybindings();
375 history_load();
376 init_bar();
377
378 scanwins();
379
380 c = read_startup_files(alt_rcfile);
381 if (c == -1)
382 return 1;
383 if (c == 0) {
384 /* No config file, just do something basic. */
385 cmdret *result;
386 if ((result = command(0, "hsplit")))
387 cmdret_free(result);
388 }
389
390 /* Indicate to the user that we have booted. */
391 if (defaults.startup_message)
392 show_welcome_message();
393
394 /* If no window has focus, give the key_window focus. */
395 if (current_window() == NULL)
396 set_window_focus(rp_current_screen->key_window);
397
398#ifdef __OpenBSD__
399 /* cpath/wpath/fattr needed for history_save() */
400 pledge("stdio rpath cpath wpath fattr unix proc exec", NULL);
401#endif
402
403 listen_for_commands();
404 listen_for_events();
405
406 return 0;
407}