jcs ratpoison hax
at master 532 lines 12 kB view raw
1/* Copyright (C) 2000, 2001, 2002, 2003, 2004 Shawn Betts <sabetts@vcn.bc.ca> 2 * 3 * This file is part of ratpoison. 4 * 5 * ratpoison 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, or (at your option) 8 * any later version. 9 * 10 * ratpoison 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 software; see the file COPYING. If not, write to 17 * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, 18 * Boston, MA 02111-1307 USA 19 */ 20 21#include "ratpoison.h" 22 23#include <sys/types.h> 24#include <sys/wait.h> 25 26#include <ctype.h> 27#include <errno.h> 28#if defined (HAVE_PWD_H) && defined (HAVE_GETPWUID) 29# include <pwd.h> 30#endif 31#include <signal.h> 32#include <unistd.h> 33 34/* Several systems seem not to have WAIT_ANY defined, so define it if 35 it isn't. */ 36#ifndef WAIT_ANY 37# define WAIT_ANY -1 38#endif 39 40/* Some systems don't define the close-on-exec flag in fcntl.h */ 41#ifndef FD_CLOEXEC 42# define FD_CLOEXEC 1 43#endif 44 45int alarm_signalled = 0; 46int kill_signalled = 0; 47int hup_signalled = 0; 48int chld_signalled = 0; 49int rat_x; 50int rat_y; 51int rat_visible = 1; /* rat is visible by default */ 52 53char *rp_exec_newwm = NULL; 54 55int rp_font_ascent, rp_font_descent, rp_font_width; 56 57Atom wm_name; 58Atom wm_state; 59Atom wm_change_state; 60Atom wm_protocols; 61Atom wm_delete; 62Atom wm_take_focus; 63Atom wm_colormaps; 64 65Atom rp_command; 66Atom rp_command_request; 67Atom rp_command_result; 68Atom rp_selection; 69 70/* TEXT atoms */ 71Atom xa_string; 72Atom xa_compound_text; 73Atom xa_utf8_string; 74 75/* netwm atoms */ 76Atom _net_wm_pid; 77Atom _net_supported; 78Atom _net_wm_window_type; 79Atom _net_wm_window_type_dialog; 80Atom _net_wm_name; 81 82LIST_HEAD (rp_screens); 83rp_screen *rp_current_screen; 84rp_global_screen rp_glob_screen; 85 86Display *dpy; 87 88int rp_have_xrandr; 89 90rp_group *rp_current_group; 91LIST_HEAD (rp_groups); 92LIST_HEAD (rp_children); 93struct rp_defaults defaults; 94 95int ignore_badwindow = 0; 96 97char **myargv; 98 99struct rp_key prefix_key; 100 101struct modifier_info rp_modifier_info; 102 103/* rudeness levels */ 104int rp_honour_transient_raise = 1; 105int rp_honour_normal_raise = 1; 106int rp_honour_transient_map = 1; 107int rp_honour_normal_map = 1; 108 109char *rp_error_msg = NULL; 110 111/* Global frame numset */ 112struct numset *rp_frame_numset; 113 114/* The X11 selection globals */ 115rp_xselection selection; 116 117static void 118x_export_selection (void) 119{ 120 rp_screen *screen; 121 122 list_first(screen, &rp_screens, node); 123 if (!screen) 124 return; 125 126 /* Hang the selections off screen 0's key window. */ 127 XSetSelectionOwner (dpy, XA_PRIMARY, screen->key_window, CurrentTime); 128 if (XGetSelectionOwner (dpy, XA_PRIMARY) != screen->key_window) 129 PRINT_ERROR(("can't get primary selection")); 130 XChangeProperty(dpy, screen->root, XA_CUT_BUFFER0, xa_string, 8, 131 PropModeReplace, (unsigned char*)selection.text, selection.len); 132} 133 134void 135set_nselection (char *txt, int len) 136{ 137 int i; 138 139 /* Update the selection structure */ 140 free (selection.text); 141 142 /* Copy the string by hand. */ 143 selection.text = xmalloc (len + 1); 144 selection.len = len + 1; 145 for (i=0; i<len; i++) 146 selection.text[i] = txt[i]; 147 selection.text[len] = 0; 148 149 x_export_selection(); 150} 151 152void 153set_selection (char *txt) 154{ 155 /* Update the selection structure */ 156 free (selection.text); 157 selection.text = xstrdup (txt); 158 selection.len = strlen (txt); 159 160 x_export_selection(); 161} 162 163static char * 164get_cut_buffer (void) 165{ 166 int nbytes; 167 char *data; 168 169 PRINT_DEBUG (("trying the cut buffer\n")); 170 171 data = XFetchBytes (dpy, &nbytes); 172 173 if (data) 174 { 175 struct sbuf *s = sbuf_new (0); 176 sbuf_nconcat (s, data, nbytes); 177 XFree (data); 178 return sbuf_free_struct (s); 179 } 180 else 181 return NULL; 182} 183 184/* Lifted the code from rxvt. */ 185static char * 186get_primary_selection(void) 187{ 188 long nread; 189 unsigned long bytes_after; 190 XTextProperty ct; 191 struct sbuf *s = sbuf_new(0); 192 193 for (nread = 0, bytes_after = 1; bytes_after > 0; nread += ct.nitems) { 194 if ((XGetWindowProperty (dpy, rp_current_screen->input_window, rp_selection, (nread / 4), 4096, 195 True, AnyPropertyType, &ct.encoding, 196 &ct.format, &ct.nitems, &bytes_after, 197 &ct.value) != Success)) { 198 XFree(ct.value); 199 sbuf_free(s); 200 return NULL; 201 } 202 if (ct.value == NULL) 203 continue; 204 /* Accumulate the data. FIXME: ct.value may not be NULL 205 terminated. */ 206 sbuf_nconcat (s, (const char*)ct.value, ct.nitems); 207 XFree(ct.value); 208 } 209 return sbuf_free_struct (s); 210} 211 212char * 213get_selection (void) 214{ 215 Atom property; 216 XEvent ev; 217 rp_screen *s = rp_current_screen; 218 int loops = 1000; 219 220 /* Just insert our text, if we own the selection. */ 221 if (selection.text) 222 { 223 return xstrdup (selection.text); 224 } 225 else 226 { 227 /* be a good icccm citizen */ 228 XDeleteProperty (dpy, s->input_window, rp_selection); 229 /* TODO: we shouldn't use CurrentTime here, use the time of the XKeyEvent, should we fake it? */ 230 XConvertSelection (dpy, XA_PRIMARY, xa_string, rp_selection, s->input_window, CurrentTime); 231 232 /* This seems like a hack. */ 233 while (!XCheckTypedWindowEvent (dpy, s->input_window, SelectionNotify, &ev)) 234 { 235 if (loops == 0) 236 { 237 PRINT_ERROR (("selection request timed out\n")); 238 return NULL; 239 } 240 usleep (10000); 241 loops--; 242 } 243 244 PRINT_DEBUG (("SelectionNotify event\n")); 245 246 property = ev.xselection.property; 247 248 if (property != None) 249 return get_primary_selection (); 250 else 251 return get_cut_buffer (); 252 } 253} 254 255/* The hook dictionary globals. */ 256 257LIST_HEAD (rp_key_hook); 258LIST_HEAD (rp_switch_win_hook); 259LIST_HEAD (rp_switch_frame_hook); 260LIST_HEAD (rp_switch_group_hook); 261LIST_HEAD (rp_switch_screen_hook); 262LIST_HEAD (rp_quit_hook); 263LIST_HEAD (rp_restart_hook); 264LIST_HEAD (rp_delete_window_hook); 265LIST_HEAD (rp_new_window_hook); 266LIST_HEAD (rp_title_changed_hook); 267 268struct rp_hook_db_entry rp_hook_db[]= 269 {{"key", &rp_key_hook}, 270 {"switchwin", &rp_switch_win_hook}, 271 {"switchframe", &rp_switch_frame_hook}, 272 {"switchgroup", &rp_switch_group_hook}, 273 {"switchscreen", &rp_switch_screen_hook}, 274 {"deletewindow", &rp_delete_window_hook}, 275 {"quit", &rp_quit_hook}, 276 {"restart", &rp_restart_hook}, 277 {"newwindow", &rp_new_window_hook}, 278 {"titlechanged", &rp_title_changed_hook}, 279 {NULL, NULL}}; 280 281void 282set_rp_window_focus (rp_window *win) 283{ 284 PRINT_DEBUG (("Giving focus to '%s'\n", window_name (win))); 285 XSetInputFocus (dpy, win->w, 286 RevertToPointerRoot, CurrentTime); 287} 288 289void 290set_window_focus (Window window) 291{ 292 PRINT_DEBUG (("Giving focus to %ld\n", window)); 293 XSetInputFocus (dpy, window, 294 RevertToPointerRoot, CurrentTime); 295} 296 297 298/* Wrapper font functions to support Xft */ 299 300void 301rp_draw_string (rp_screen *s, Drawable d, int style, int x, int y, 302 char *string, int length) 303{ 304 if (length < 0) 305 length = strlen (string); 306 307#ifdef USE_XFT_FONT 308 if (s->xft_font) 309 { 310 XftDraw *draw; 311 draw = XftDrawCreate (dpy, d, DefaultVisual (dpy, s->screen_num), 312 DefaultColormap (dpy, s->screen_num)); 313 if (!draw) 314 { 315 PRINT_ERROR (("Failed to allocate XftDraw object\n")); 316 return; 317 } 318 319 if (utf8_locale) 320 { 321 XftDrawStringUtf8 (draw, style == STYLE_NORMAL ? &s->xft_fg_color : 322 &s->xft_bg_color, s->xft_font, x, y, 323 (FcChar8*) string, length); 324 } 325 else 326 { 327 XftDrawString8 (draw, style == STYLE_NORMAL ? &s->xft_fg_color : 328 &s->xft_bg_color, s->xft_font, x, y, 329 (FcChar8*) string, length); 330 } 331 XftDrawDestroy (draw); 332 } 333 else 334 PRINT_ERROR (("No Xft font available.\n")); 335#else 336 XmbDrawString (dpy, d, defaults.font, style == STYLE_NORMAL ? s->normal_gc : 337 s->inverse_gc, x, y, string, length); 338#endif 339} 340 341int 342rp_text_width (rp_screen *s, char *string, int count) 343{ 344 (void) s; /* avoid "unused" warning */ 345 if (count < 0) 346 count = strlen (string); 347 348#ifdef USE_XFT_FONT 349 if (s->xft_font) 350 { 351 XGlyphInfo extents; 352 if (utf8_locale) 353 XftTextExtentsUtf8 (dpy, s->xft_font, (FcChar8*) string, count, &extents); 354 else 355 XftTextExtents8 (dpy, s->xft_font, (FcChar8*) string, count, &extents); 356 return extents.xOff; 357 } 358 PRINT_ERROR (("No Xft font available.\n")); 359 return 0; 360#else 361 return XmbTextEscapement (defaults.font, string, count); 362#endif 363} 364 365/* A case insensitive strncmp. */ 366int 367str_comp (char *s1, char *s2, size_t len) 368{ 369 size_t i; 370 371 for (i = 0; i < len; i++) 372 if (toupper ((unsigned char)s1[i]) != toupper ((unsigned char)s2[i])) 373 return 0; 374 375 return 1; 376} 377 378/* Check for child processes that have quit but haven't been 379 acknowledged yet. Update their structure. */ 380void 381check_child_procs (void) 382{ 383 rp_child_info *cur; 384 int pid, status; 385 while (1) 386 { 387 pid = waitpid (WAIT_ANY, &status, WNOHANG); 388 if (pid <= 0) 389 break; 390 391 PRINT_DEBUG(("Child status: %d\n", WEXITSTATUS (status))); 392 393 /* Find the child and update its structure. */ 394 list_for_each_entry (cur, &rp_children, node) 395 { 396 if (cur->pid == pid) 397 { 398 cur->terminated = 1; 399 cur->status = WEXITSTATUS (status); 400 break; 401 } 402 } 403 404 chld_signalled = 1; 405 } 406} 407 408void 409chld_handler (int signum UNUSED) 410{ 411 int serrno; 412 413 serrno = errno; 414 check_child_procs(); 415 errno = serrno; 416} 417 418void 419set_sig_handler (int sig, void (*action)(int)) 420{ 421 struct sigaction act; 422 423 memset (&act, 0, sizeof (act)); 424 act.sa_handler = action; 425 sigemptyset (&act.sa_mask); 426 if (sigaction (sig, &act, NULL)) 427 { 428 PRINT_ERROR (("Error setting signal handler: %s\n", 429 strerror (errno))); 430 } 431} 432 433void 434set_close_on_exec (int fd) 435{ 436 int flags = fcntl (fd, F_GETFD); 437 if (flags >= 0) 438 fcntl (fd, F_SETFD, flags | FD_CLOEXEC); 439} 440 441void 442read_rc_file (FILE *file) 443{ 444 char *line; 445 size_t linesize = 256; 446 447 line = xmalloc (linesize); 448 449 while (getline (&line, &linesize, file) != -1) 450 { 451 line[strcspn (line, "\n")] = '\0'; 452 453 PRINT_DEBUG (("rcfile line: %s\n", line)); 454 455 if (*line != '\0' && *line != '#') 456 { 457 cmdret *result; 458 result = command (0, line); 459 460 /* Gobble the result. */ 461 if (result) 462 cmdret_free (result); 463 } 464 } 465 466 free (line); 467} 468 469const char * 470get_homedir (void) 471{ 472 char *homedir; 473 474 homedir = getenv ("HOME"); 475 if (homedir != NULL && homedir[0] == '\0') 476 homedir = NULL; 477 478#if defined (HAVE_PWD_H) && defined (HAVE_GETPWUID) 479 if (homedir == NULL) 480 { 481 struct passwd *pw; 482 483 pw = getpwuid (getuid ()); 484 if (pw != NULL) 485 homedir = pw->pw_dir; 486 487 if (homedir != NULL && homedir[0] == '\0') 488 homedir = NULL; 489 } 490#endif 491 492 return homedir; 493} 494 495void 496clean_up (void) 497{ 498 rp_screen *cur; 499 struct list_head *iter, *tmp; 500 501 history_save (); 502 503 free_keymaps (); 504 free_aliases (); 505 free_user_commands (); 506 free_bar (); 507 free_window_stuff (); 508 free_groups (); 509 510 list_for_each_safe_entry (cur, iter, tmp, &rp_screens, node) 511 { 512 list_del (&cur->node); 513 screen_free (cur); 514 free (cur); 515 } 516 517 screen_free_final (); 518 519 /* Delete the undo histories */ 520 clear_frame_undos (); 521 522 /* Free the global frame numset shared by all screens. */ 523 numset_free (rp_frame_numset); 524 525#ifndef USE_XFT_FONT 526 XFreeFontSet (dpy, defaults.font); 527#endif 528 free (defaults.window_fmt); 529 530 XSetInputFocus (dpy, PointerRoot, RevertToPointerRoot, CurrentTime); 531 XCloseDisplay (dpy); 532}