A tiling window manager
at master 407 lines 10 kB view raw
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}