jcs ratpoison hax
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}