jcs ratpoison hax
1/* Copyright (C) 2000, 2001, 2002, 2003, 2004 Shawn Betts <sabetts@vcn.bc.ca>
2 * Copyright (C) 2016 Mathieu OTHACEHE <m.othacehe@gmail.com>
3 *
4 * This file is part of ratpoison.
5 *
6 * ratpoison is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2, or (at your option)
9 * any later version.
10 *
11 * ratpoison is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this software; see the file COPYING. If not, write to
18 * the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
19 * Boston, MA 02111-1307 USA
20 */
21
22#include "ratpoison.h"
23#include <string.h>
24#include <X11/cursorfont.h>
25
26static void init_screen (rp_screen *s);
27
28int
29screen_width (rp_screen *s)
30{
31 return s->width - defaults.padding_right - defaults.padding_left;
32}
33
34int
35screen_height (rp_screen *s)
36{
37 return s->height - defaults.padding_bottom - defaults.padding_top;
38}
39
40int
41screen_left (rp_screen *s)
42{
43 return s->left + defaults.padding_left;
44}
45
46int
47screen_right (rp_screen *s)
48{
49 return screen_left (s) + screen_width (s);
50}
51
52int
53screen_top (rp_screen *s)
54{
55 return s->top + defaults.padding_top;
56}
57
58int
59screen_bottom (rp_screen *s)
60{
61 return screen_top (s) + screen_height (s);
62}
63
64/* Returns a pointer to a list of frames. */
65struct list_head *
66screen_copy_frameset (rp_screen *s)
67{
68 struct list_head *head;
69 rp_frame *cur;
70
71 /* Init our new list. */
72 head = xmalloc (sizeof (struct list_head));
73 INIT_LIST_HEAD (head);
74
75 /* Copy each frame to our new list. */
76 list_for_each_entry (cur, &s->frames, node)
77 {
78 list_add_tail (&(frame_copy (cur))->node, head);
79 }
80
81 return head;
82}
83
84/* Set head as the frameset, deleting the existing one. */
85void
86screen_restore_frameset (rp_screen *s, struct list_head *head)
87{
88 frameset_free (&s->frames);
89 INIT_LIST_HEAD (&s->frames);
90
91 /* Hook in our new frameset. */
92 list_splice (head, &s->frames);
93}
94
95
96/* Given a screen, free the frames' numbers from the numset. */
97void
98screen_free_nums (rp_screen *s)
99{
100 rp_frame *cur;
101
102 list_for_each_entry (cur, &s->frames, node)
103 {
104 numset_release (s->frames_numset, cur->number);
105 }
106}
107
108/* Given a list of frames, free them, but don't remove their numbers
109 from the numset. */
110void
111frameset_free (struct list_head *head)
112{
113 rp_frame *frame;
114 struct list_head *iter, *tmp;
115
116 list_for_each_safe_entry (frame, iter, tmp, head, node)
117 {
118 /* FIXME: what if frames has memory inside its struct
119 that needs to be freed? */
120 free (frame);
121 }
122}
123
124rp_frame *
125screen_get_frame (rp_screen *s, int frame_num)
126{
127 rp_frame *cur;
128
129 list_for_each_entry (cur, &s->frames, node)
130 {
131 if (cur->number == frame_num)
132 return cur;
133 }
134
135 return NULL;
136}
137
138rp_frame *
139screen_find_frame_by_frame (rp_screen *s, rp_frame *f)
140{
141 rp_frame *cur;
142
143 list_for_each_entry (cur, &s->frames, node)
144 {
145 PRINT_DEBUG (("cur=%p f=%p\n", cur, f));
146 if (cur == f)
147 return cur;
148 }
149
150 return NULL;
151}
152
153/* Given a root window, return the rp_screen struct */
154rp_screen *
155find_screen (Window w)
156{
157 rp_screen *cur;
158
159 list_for_each_entry (cur, &rp_screens, node)
160 {
161 if (cur->root == w)
162 return cur;
163 }
164
165 return NULL;
166}
167
168/* Given a window attr, return the rp_screen struct */
169rp_screen *
170find_screen_by_attr (XWindowAttributes attr)
171{
172 rp_screen *cur;
173
174 list_for_each_entry (cur, &rp_screens, node)
175 {
176 if (attr.x >= cur->left &&
177 attr.x <= cur->left + cur->width &&
178 attr.y >= cur->top &&
179 attr.y <= cur->top + cur->height)
180 return cur;
181 }
182
183 return NULL;
184}
185
186/* Return 1 if w is a root window of any of the screens. */
187int
188is_a_root_window (unsigned int w)
189{
190 rp_screen *cur;
191
192 list_for_each_entry (cur, &rp_screens, node)
193 {
194 if (cur->root == w)
195 return 1;
196 }
197
198 return 0;
199}
200
201rp_screen *
202screen_number (int number)
203{
204 rp_screen *cur;
205
206 list_for_each_entry (cur, &rp_screens, node)
207 {
208 if (cur->number == number)
209 return cur;
210 }
211
212 return NULL;
213}
214
215static int
216screen_cmp (void *priv UNUSED, struct list_head *a, struct list_head *b)
217{
218 rp_screen *sc_a = container_of (a, typeof(*sc_a), node);
219 rp_screen *sc_b = container_of (b, typeof(*sc_b), node);
220
221 if (sc_a->left < sc_b->left)
222 return -1;
223 if (sc_a->left > sc_b->left)
224 return 1;
225
226 if (sc_a->top > sc_b->top)
227 return -1;
228 if (sc_a->top < sc_b->top)
229 return 1;
230
231 return 0;
232}
233
234void
235screen_sort (void)
236{
237 return list_sort (NULL, &rp_screens, screen_cmp);
238}
239
240static void
241screen_set_numbers (void)
242{
243 rp_screen *cur;
244
245 list_for_each_entry (cur, &rp_screens, node)
246 {
247 cur->number = numset_request (rp_glob_screen.numset);
248 }
249}
250
251static void
252screen_select_primary (void)
253{
254 rp_screen *cur;
255
256 /* By default, take the first screen as current screen */
257 list_first(cur, &rp_screens, node);
258 if (!rp_current_screen)
259 rp_current_screen = cur;
260
261 if (!rp_have_xrandr)
262 return;
263
264 list_for_each_entry (cur, &rp_screens, node)
265 {
266 if (xrandr_is_primary(cur)) {
267 rp_current_screen = cur;
268 PRINT_DEBUG(("Xrandr primary screen %d detected\n",
269 rp_current_screen->number));
270 break;
271 }
272 }
273}
274
275static void
276init_global_screen (rp_global_screen *s)
277{
278 int screen_num;
279
280 screen_num = DefaultScreen (dpy);
281 s->root = RootWindow (dpy, screen_num);
282
283 s->numset = numset_new ();
284 s->fg_color = BlackPixel (dpy, screen_num);
285 s->bg_color = WhitePixel (dpy, screen_num);
286 s->fw_color = BlackPixel (dpy, screen_num);
287 s->bw_color = BlackPixel (dpy, screen_num);
288}
289
290void
291init_screens (void)
292{
293 int i;
294 int screen_count = 0;
295 int *rr_outputs = NULL;
296 rp_screen *screen;
297
298 /* Get the number of screens */
299 if (rp_have_xrandr)
300 screen_count = xrandr_query_screen (&rr_outputs);
301 else
302 screen_count = ScreenCount (dpy);
303
304 /* Create our global frame numset */
305 rp_frame_numset = numset_new();
306
307 init_global_screen (&rp_glob_screen);
308
309 for (i = 0; i < screen_count; i++)
310 {
311 screen = xmalloc (sizeof(*screen));
312 list_add (&screen->node, &rp_screens);
313
314 if (rp_have_xrandr)
315 xrandr_fill_screen (rr_outputs[i], screen);
316 else
317 xrandr_fill_screen (i, screen);
318
319 init_screen (screen);
320 }
321
322 screen_sort ();
323 screen_set_numbers ();
324 screen_select_primary ();
325
326 free (rr_outputs);
327}
328
329static void
330init_rat_cursor (rp_screen *s)
331{
332 s->rat = XCreateFontCursor (dpy, XC_icon);
333}
334
335static void
336init_screen (rp_screen *s)
337{
338 XGCValues gcv;
339 struct sbuf *buf;
340 char *colon;
341 int screen_num;
342
343 screen_num = DefaultScreen (dpy);
344
345 if (!rp_have_xrandr)
346 {
347 s->left = 0;
348 s->top = 0;
349 s->width = DisplayWidth (dpy, screen_num);
350 s->height = DisplayHeight (dpy, screen_num);
351 }
352
353 /* Select on some events on the root window, if this fails, then
354 there is already a WM running and the X Error handler will catch
355 it, terminating ratpoison. */
356 XSelectInput (dpy, RootWindow (dpy, screen_num),
357 PropertyChangeMask | ColormapChangeMask
358 | SubstructureRedirectMask | SubstructureNotifyMask
359 | StructureNotifyMask);
360 XSync (dpy, False);
361
362 /* Set the numset for the frames to our global numset. */
363 s->frames_numset = rp_frame_numset;
364
365 s->scratch_buffer = NULL;
366
367 /* Build the display string for each screen */
368 buf = sbuf_new (0);
369 sbuf_printf (buf, "DISPLAY=%s", DisplayString (dpy));
370 colon = strrchr (sbuf_get (buf), ':');
371 if (colon)
372 {
373 char *dot;
374
375 dot = strrchr (sbuf_get (buf), '.');
376 if (!dot || dot < colon)
377 {
378 /* no dot was found or it belongs to fqdn - append screen_num
379 to the end */
380 sbuf_printf_concat (buf, ".%d", screen_num);
381 }
382 }
383 s->display_string = sbuf_free_struct (buf);
384
385 PRINT_DEBUG (("display string: %s\n", s->display_string));
386
387 s->root = RootWindow (dpy, screen_num);
388 s->screen_num = screen_num;
389 s->def_cmap = DefaultColormap (dpy, screen_num);
390
391 init_rat_cursor (s);
392
393 /* Setup the GC for drawing the font. */
394 gcv.foreground = rp_glob_screen.fg_color;
395 gcv.background = rp_glob_screen.bg_color;
396 gcv.function = GXcopy;
397 gcv.line_width = 1;
398 gcv.subwindow_mode = IncludeInferiors;
399 s->normal_gc = XCreateGC(dpy, s->root,
400 GCForeground | GCBackground | GCFunction
401 | GCLineWidth | GCSubwindowMode,
402 &gcv);
403 gcv.foreground = rp_glob_screen.bg_color;
404 gcv.background = rp_glob_screen.fg_color;
405 s->inverse_gc = XCreateGC(dpy, s->root,
406 GCForeground | GCBackground | GCFunction
407 | GCLineWidth | GCSubwindowMode,
408 &gcv);
409
410 /* Create the program bar window. */
411 s->bar_is_raised = 0;
412 s->bar_window = XCreateSimpleWindow (dpy, s->root, 0, 0, 1, 1,
413 defaults.bar_border_width,
414 rp_glob_screen.fg_color, rp_glob_screen.bg_color);
415
416 /* Setup the window that will receive all keystrokes once the prefix
417 key has been pressed. */
418 s->key_window = XCreateSimpleWindow (dpy, s->root, 0, 0, 1, 1, 0,
419 WhitePixel (dpy, screen_num),
420 BlackPixel (dpy, screen_num));
421 XSelectInput (dpy, s->key_window, KeyPressMask | KeyReleaseMask);
422
423 /* Create the input window. */
424 s->input_window = XCreateSimpleWindow (dpy, s->root, 0, 0, 1, 1,
425 defaults.bar_border_width,
426 rp_glob_screen.fg_color, rp_glob_screen.bg_color);
427 XSelectInput (dpy, s->input_window, KeyPressMask | KeyReleaseMask);
428
429 /* Create the frame indicator window */
430 s->frame_window = XCreateSimpleWindow (dpy, s->root, 1, 1, 1, 1, defaults.bar_border_width,
431 rp_glob_screen.fg_color, rp_glob_screen.bg_color);
432
433 /* Create the help window */
434 s->help_window = XCreateSimpleWindow (dpy, s->root, s->left, s->top, s->width,
435 s->height, 0, rp_glob_screen.fg_color, rp_glob_screen.bg_color);
436 XSelectInput (dpy, s->help_window, KeyPressMask);
437
438 activate_screen(s);
439
440 XSync (dpy, 0);
441
442#ifdef USE_XFT_FONT
443 {
444 s->xft_font = XftFontOpenName (dpy, screen_num, DEFAULT_XFT_FONT);
445 if (!s->xft_font)
446 {
447 PRINT_ERROR(("Failed to open font\n"));
448 }
449 else
450 {
451 if (!XftColorAllocName (dpy, DefaultVisual (dpy, screen_num),
452 DefaultColormap (dpy, screen_num),
453 defaults.fgcolor_string, &s->xft_fg_color))
454 {
455 PRINT_ERROR(("Failed to allocate font fg color\n"));
456 XftFontClose (dpy, s->xft_font);
457 s->xft_font = NULL;
458 }
459 if (!XftColorAllocName (dpy, DefaultVisual (dpy, screen_num),
460 DefaultColormap (dpy, screen_num),
461 defaults.bgcolor_string, &s->xft_bg_color))
462 {
463 PRINT_ERROR(("Failed to allocate font fg color\n"));
464 XftFontClose (dpy, s->xft_font);
465 s->xft_font = NULL;
466 }
467 }
468 }
469#endif
470}
471
472void
473activate_screen (rp_screen *s)
474{
475 /* Add netwm support. FIXME: I think this is busted. */
476 XChangeProperty (dpy, RootWindow (dpy, s->screen_num),
477 _net_supported, XA_ATOM, 32, PropModeReplace,
478 (unsigned char*)&_net_wm_pid, 1);
479
480 /* set window manager name */
481 XChangeProperty (dpy, RootWindow (dpy, s->screen_num),
482 _net_wm_name, xa_utf8_string, 8, PropModeReplace,
483 (unsigned char*)"ratpoison", 9);
484 XMapWindow (dpy, s->key_window);
485}
486
487void
488deactivate_screen (rp_screen *s)
489{
490 /* Unmap its key window */
491 XUnmapWindow (dpy, s->key_window);
492
493 /* delete everything so noone sees them while we are not there */
494 XDeleteProperty (dpy, RootWindow (dpy, s->screen_num),
495 _net_supported);
496 XDeleteProperty (dpy, RootWindow (dpy, s->screen_num),
497 _net_wm_name);
498}
499
500static int
501is_rp_window_for_given_screen (Window w, rp_screen *s)
502{
503 if (w != s->key_window &&
504 w != s->bar_window &&
505 w != s->input_window &&
506 w != s->frame_window &&
507 w != s->help_window)
508 return 0;
509 return 1;
510}
511
512int
513is_rp_window (Window w)
514{
515 rp_screen *cur;
516
517 list_for_each_entry (cur, &rp_screens, node)
518 {
519 if (is_rp_window_for_given_screen (w, cur))
520 return 1;
521 }
522
523 return 0;
524}
525
526char *
527screen_dump (rp_screen *screen)
528{
529 char *tmp;
530 struct sbuf *s;
531
532 s = sbuf_new (0);
533 if (rp_have_xrandr)
534 sbuf_printf(s, "%s ", screen->xrandr.name);
535
536 sbuf_printf_concat (s, "%d %d %d %d %d %d",
537 screen->number,
538 screen->left,
539 screen->top,
540 screen->width,
541 screen->height,
542 (rp_current_screen == screen)?1:0 /* is current? */
543 );
544
545 /* Extract the string and return it, and don't forget to free s. */
546 tmp = sbuf_get (s);
547 free (s);
548 return tmp;
549}
550
551int
552screen_count (void)
553{
554 return list_size (&rp_screens);
555}
556
557rp_screen *
558screen_next (void)
559{
560 return list_next_entry (rp_current_screen, &rp_screens, node);
561}
562
563rp_screen *
564screen_prev (void)
565{
566 return list_prev_entry (rp_current_screen, &rp_screens, node);
567}
568
569static void
570screen_remove_current (void)
571{
572 rp_screen *new_screen;
573 rp_frame *new_frame;
574 rp_window *cur_win;
575 int cur_frame;
576
577 cur_win = current_window ();
578 new_screen = screen_next ();
579
580 cur_frame = new_screen->current_frame;
581 new_frame = screen_get_frame (new_screen, cur_frame);
582
583 set_active_frame (new_frame, 1);
584
585 hide_window (cur_win);
586}
587
588void
589screen_update (rp_screen *s, int left, int top, int width, int height)
590{
591 rp_frame *f;
592 int oldwidth, oldheight;
593
594 PRINT_DEBUG (("screen_update (left=%d, top=%d, width=%d, height=%d)\n",
595 left, top, width, height));
596
597 if (s->width == width &&
598 s->height == height &&
599 s->left == left &&
600 s->top == top)
601 /* nothing to do */
602 return;
603
604 oldwidth = s->width;
605 oldheight = s->height;
606
607 s->left = left;
608 s->top = top;
609 s->width = width;
610 s->height = height;
611
612 XMoveResizeWindow (dpy, s->help_window, s->left, s->top, s->width, s->height);
613
614 list_for_each_entry (f, &s->frames, node)
615 {
616 f->x = (f->x*width)/oldwidth;
617 f->width = (f->width*width)/oldwidth;
618 f->y = (f->y*height)/oldheight;
619 f->height = (f->height*height)/oldheight;
620 maximize_all_windows_in_frame (f);
621 }
622}
623
624rp_screen *
625screen_add (int rr_output)
626{
627 rp_screen *screen;
628
629 screen = xmalloc (sizeof(*screen));
630 list_add (&screen->node, &rp_screens);
631
632 screen->number = numset_request (rp_glob_screen.numset);
633
634 xrandr_fill_screen (rr_output, screen);
635 init_screen (screen);
636 init_frame_list (screen);
637
638 if (screen_count () == 1)
639 {
640 rp_current_screen = screen;
641 change_windows_screen (NULL, rp_current_screen);
642 set_window_focus (rp_current_screen->key_window);
643 }
644
645 return screen;
646}
647
648void
649screen_del (rp_screen *s)
650{
651 if (s == rp_current_screen)
652 {
653 if (screen_count () == 1)
654 {
655 hide_screen_windows (s);
656 rp_current_screen = NULL;
657 }
658 else
659 {
660 /*
661 * The deleted screen cannot be the current screen anymore,
662 * focus the next one.
663 */
664 screen_remove_current ();
665 }
666 }
667 else
668 {
669 hide_screen_windows (s);
670 }
671
672 /* Affect window's screen backpointer to the new current screen */
673 change_windows_screen (s, rp_current_screen);
674
675 numset_release (rp_glob_screen.numset, s->number);
676
677 screen_free (s);
678
679 list_del (&s->node);
680 free (s);
681}
682
683void
684screen_free (rp_screen *s)
685{
686 rp_frame *frame;
687 struct list_head *iter, *tmp;
688
689 list_for_each_safe_entry (frame, iter, tmp, &s->frames, node)
690 {
691 frame_free (s, frame);
692 }
693
694 deactivate_screen(s);
695
696 XDestroyWindow (dpy, s->bar_window);
697 XDestroyWindow (dpy, s->key_window);
698 XDestroyWindow (dpy, s->input_window);
699 XDestroyWindow (dpy, s->frame_window);
700 XDestroyWindow (dpy, s->help_window);
701
702#ifdef USE_XFT_FONT
703 if (s->xft_font)
704 {
705 XftColorFree (dpy, DefaultVisual (dpy, s->screen_num),
706 DefaultColormap (dpy, s->screen_num), &s->xft_fg_color);
707 XftColorFree (dpy, DefaultVisual (dpy, s->screen_num),
708 DefaultColormap (dpy, s->screen_num), &s->xft_bg_color);
709 XftFontClose (dpy, s->xft_font);
710 }
711#endif
712
713 XFreeCursor (dpy, s->rat);
714 XFreeColormap (dpy, s->def_cmap);
715 XFreeGC (dpy, s->normal_gc);
716 XFreeGC (dpy, s->inverse_gc);
717
718 free (s->display_string);
719 free (s->xrandr.name);
720}
721
722void
723screen_free_final (void)
724{
725 /* Relinquish our hold on the root window. */
726 XSelectInput(dpy, RootWindow (dpy, DefaultScreen (dpy)), 0);
727
728 numset_free (rp_glob_screen.numset);
729}