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 long val;
341 char *colon;
342 int screen_num;
343
344 screen_num = DefaultScreen (dpy);
345
346 if (!rp_have_xrandr)
347 {
348 s->left = 0;
349 s->top = 0;
350 s->width = DisplayWidth (dpy, screen_num);
351 s->height = DisplayHeight (dpy, screen_num);
352 }
353
354 /* Select on some events on the root window, if this fails, then
355 there is already a WM running and the X Error handler will catch
356 it, terminating ratpoison. */
357 XSelectInput (dpy, RootWindow (dpy, screen_num),
358 PropertyChangeMask | ColormapChangeMask
359 | SubstructureRedirectMask | SubstructureNotifyMask
360 | StructureNotifyMask);
361 XSync (dpy, False);
362
363 /* Set the numset for the frames to our global numset. */
364 s->frames_numset = rp_frame_numset;
365
366 s->scratch_buffer = NULL;
367
368 /* Build the display string for each screen */
369 buf = sbuf_new (0);
370 sbuf_printf (buf, "DISPLAY=%s", DisplayString (dpy));
371 colon = strrchr (sbuf_get (buf), ':');
372 if (colon)
373 {
374 char *dot;
375
376 dot = strrchr (sbuf_get (buf), '.');
377 if (!dot || dot < colon)
378 {
379 /* no dot was found or it belongs to fqdn - append screen_num
380 to the end */
381 sbuf_printf_concat (buf, ".%d", screen_num);
382 }
383 }
384 s->display_string = sbuf_free_struct (buf);
385
386 PRINT_DEBUG (("display string: %s\n", s->display_string));
387
388 s->root = RootWindow (dpy, screen_num);
389 s->screen_num = screen_num;
390 s->def_cmap = DefaultColormap (dpy, screen_num);
391
392 init_rat_cursor (s);
393
394 /* Setup the GC for drawing the font. */
395 gcv.foreground = rp_glob_screen.fg_color;
396 gcv.background = rp_glob_screen.bg_color;
397 gcv.function = GXcopy;
398 gcv.line_width = 1;
399 gcv.subwindow_mode = IncludeInferiors;
400 s->normal_gc = XCreateGC(dpy, s->root,
401 GCForeground | GCBackground | GCFunction
402 | GCLineWidth | GCSubwindowMode,
403 &gcv);
404 gcv.foreground = rp_glob_screen.bg_color;
405 gcv.background = rp_glob_screen.fg_color;
406 s->inverse_gc = XCreateGC(dpy, s->root,
407 GCForeground | GCBackground | GCFunction
408 | GCLineWidth | GCSubwindowMode,
409 &gcv);
410
411 /* Create the program bar window. */
412 s->bar_is_raised = 0;
413 s->bar_window = XCreateSimpleWindow (dpy, s->root, 0, 0, 1, 1,
414 defaults.bar_border_width,
415 rp_glob_screen.fg_color, rp_glob_screen.bg_color);
416 val = _net_wm_window_type_dock;
417 XChangeProperty (dpy, s->bar_window, _net_wm_window_type, XA_ATOM, 32,
418 PropModeReplace, (unsigned char *)&val, 1);
419
420 /* Setup the window that will receive all keystrokes once the prefix
421 key has been pressed. */
422 s->key_window = XCreateSimpleWindow (dpy, s->root, 0, 0, 1, 1, 0,
423 WhitePixel (dpy, screen_num),
424 BlackPixel (dpy, screen_num));
425 XSelectInput (dpy, s->key_window, KeyPressMask | KeyReleaseMask);
426
427 /* Create the input window. */
428 s->input_window = XCreateSimpleWindow (dpy, s->root, 0, 0, 1, 1,
429 defaults.bar_border_width,
430 rp_glob_screen.fg_color, rp_glob_screen.bg_color);
431 XSelectInput (dpy, s->input_window, KeyPressMask | KeyReleaseMask);
432 XChangeProperty (dpy, s->input_window, _net_wm_window_type, XA_ATOM, 32,
433 PropModeReplace, (unsigned char *)&val, 1);
434
435 /* Create the frame indicator window */
436 s->frame_window = XCreateSimpleWindow (dpy, s->root, 1, 1, 1, 1, defaults.bar_border_width,
437 rp_glob_screen.fg_color, rp_glob_screen.bg_color);
438 val = _net_wm_window_type_tooltip;
439 XChangeProperty (dpy, s->frame_window, _net_wm_window_type, XA_ATOM, 32,
440 PropModeReplace, (unsigned char *)&val, 1);
441
442 /* Create the help window */
443 s->help_window = XCreateSimpleWindow (dpy, s->root, s->left, s->top, s->width,
444 s->height, 0, rp_glob_screen.fg_color, rp_glob_screen.bg_color);
445 XSelectInput (dpy, s->help_window, KeyPressMask);
446
447 activate_screen(s);
448
449 XSync (dpy, 0);
450
451#ifdef USE_XFT_FONT
452 {
453 s->xft_font = XftFontOpenName (dpy, screen_num, DEFAULT_XFT_FONT);
454 if (!s->xft_font)
455 {
456 PRINT_ERROR(("Failed to open font\n"));
457 }
458 else
459 {
460 if (!XftColorAllocName (dpy, DefaultVisual (dpy, screen_num),
461 DefaultColormap (dpy, screen_num),
462 defaults.fgcolor_string, &s->xft_fg_color))
463 {
464 PRINT_ERROR(("Failed to allocate font fg color\n"));
465 XftFontClose (dpy, s->xft_font);
466 s->xft_font = NULL;
467 }
468 if (!XftColorAllocName (dpy, DefaultVisual (dpy, screen_num),
469 DefaultColormap (dpy, screen_num),
470 defaults.bgcolor_string, &s->xft_bg_color))
471 {
472 PRINT_ERROR(("Failed to allocate font fg color\n"));
473 XftFontClose (dpy, s->xft_font);
474 s->xft_font = NULL;
475 }
476 }
477 }
478#endif
479}
480
481void
482activate_screen (rp_screen *s)
483{
484 /* Add netwm support. FIXME: I think this is busted. */
485 XChangeProperty (dpy, RootWindow (dpy, s->screen_num),
486 _net_supported, XA_ATOM, 32, PropModeReplace,
487 (unsigned char*)&_net_wm_pid, 1);
488
489 /* set window manager name */
490 XChangeProperty (dpy, RootWindow (dpy, s->screen_num),
491 _net_wm_name, xa_utf8_string, 8, PropModeReplace,
492 (unsigned char*)"ratpoison", 9);
493 XMapWindow (dpy, s->key_window);
494}
495
496void
497deactivate_screen (rp_screen *s)
498{
499 /* Unmap its key window */
500 XUnmapWindow (dpy, s->key_window);
501
502 /* delete everything so noone sees them while we are not there */
503 XDeleteProperty (dpy, RootWindow (dpy, s->screen_num),
504 _net_supported);
505 XDeleteProperty (dpy, RootWindow (dpy, s->screen_num),
506 _net_wm_name);
507}
508
509static int
510is_rp_window_for_given_screen (Window w, rp_screen *s)
511{
512 if (w != s->key_window &&
513 w != s->bar_window &&
514 w != s->input_window &&
515 w != s->frame_window &&
516 w != s->help_window)
517 return 0;
518 return 1;
519}
520
521int
522is_rp_window (Window w)
523{
524 rp_screen *cur;
525
526 list_for_each_entry (cur, &rp_screens, node)
527 {
528 if (is_rp_window_for_given_screen (w, cur))
529 return 1;
530 }
531
532 return 0;
533}
534
535char *
536screen_dump (rp_screen *screen)
537{
538 char *tmp;
539 struct sbuf *s;
540
541 s = sbuf_new (0);
542 if (rp_have_xrandr)
543 sbuf_printf(s, "%s ", screen->xrandr.name);
544
545 sbuf_printf_concat (s, "%d %d %d %d %d %d",
546 screen->number,
547 screen->left,
548 screen->top,
549 screen->width,
550 screen->height,
551 (rp_current_screen == screen)?1:0 /* is current? */
552 );
553
554 /* Extract the string and return it, and don't forget to free s. */
555 tmp = sbuf_get (s);
556 free (s);
557 return tmp;
558}
559
560int
561screen_count (void)
562{
563 return list_size (&rp_screens);
564}
565
566rp_screen *
567screen_next (void)
568{
569 return list_next_entry (rp_current_screen, &rp_screens, node);
570}
571
572rp_screen *
573screen_prev (void)
574{
575 return list_prev_entry (rp_current_screen, &rp_screens, node);
576}
577
578static void
579screen_remove_current (void)
580{
581 rp_screen *new_screen;
582 rp_frame *new_frame;
583 rp_window *cur_win;
584 int cur_frame;
585
586 cur_win = current_window ();
587 new_screen = screen_next ();
588
589 cur_frame = new_screen->current_frame;
590 new_frame = screen_get_frame (new_screen, cur_frame);
591
592 set_active_frame (new_frame, 1);
593
594 hide_window (cur_win);
595}
596
597void
598screen_update (rp_screen *s, int left, int top, int width, int height)
599{
600 rp_frame *f;
601 int oldwidth, oldheight;
602
603 PRINT_DEBUG (("screen_update (left=%d, top=%d, width=%d, height=%d)\n",
604 left, top, width, height));
605
606 if (s->width == width &&
607 s->height == height &&
608 s->left == left &&
609 s->top == top)
610 /* nothing to do */
611 return;
612
613 oldwidth = s->width;
614 oldheight = s->height;
615
616 s->left = left;
617 s->top = top;
618 s->width = width;
619 s->height = height;
620
621 XMoveResizeWindow (dpy, s->help_window, s->left, s->top, s->width, s->height);
622 if (defaults.bar_sticky)
623 hide_bar (s);
624
625 list_for_each_entry (f, &s->frames, node)
626 {
627 f->x = (f->x*width)/oldwidth;
628 f->width = (f->width*width)/oldwidth;
629 f->y = (f->y*height)/oldheight;
630 f->height = (f->height*height)/oldheight;
631 maximize_all_windows_in_frame (f);
632 }
633}
634
635rp_screen *
636screen_add (int rr_output)
637{
638 rp_screen *screen;
639
640 screen = xmalloc (sizeof(*screen));
641 list_add (&screen->node, &rp_screens);
642
643 screen->number = numset_request (rp_glob_screen.numset);
644
645 xrandr_fill_screen (rr_output, screen);
646 init_screen (screen);
647 init_frame_list (screen);
648
649 if (screen_count () == 1)
650 {
651 rp_current_screen = screen;
652 change_windows_screen (NULL, rp_current_screen);
653 set_window_focus (rp_current_screen->key_window);
654 }
655
656 return screen;
657}
658
659void
660screen_del (rp_screen *s)
661{
662 if (s == rp_current_screen)
663 {
664 if (screen_count () == 1)
665 {
666 hide_screen_windows (s);
667 rp_current_screen = NULL;
668 }
669 else
670 {
671 /*
672 * The deleted screen cannot be the current screen anymore,
673 * focus the next one.
674 */
675 screen_remove_current ();
676 }
677 }
678 else
679 {
680 hide_screen_windows (s);
681 }
682
683 /* Affect window's screen backpointer to the new current screen */
684 change_windows_screen (s, rp_current_screen);
685
686 numset_release (rp_glob_screen.numset, s->number);
687
688 screen_free (s);
689
690 list_del (&s->node);
691 free (s);
692}
693
694void
695screen_free (rp_screen *s)
696{
697 rp_frame *frame;
698 struct list_head *iter, *tmp;
699
700 list_for_each_safe_entry (frame, iter, tmp, &s->frames, node)
701 {
702 frame_free (s, frame);
703 }
704
705 deactivate_screen(s);
706
707 XDestroyWindow (dpy, s->bar_window);
708 XDestroyWindow (dpy, s->key_window);
709 XDestroyWindow (dpy, s->input_window);
710 XDestroyWindow (dpy, s->frame_window);
711 XDestroyWindow (dpy, s->help_window);
712
713#ifdef USE_XFT_FONT
714 if (s->xft_font)
715 {
716 XftColorFree (dpy, DefaultVisual (dpy, s->screen_num),
717 DefaultColormap (dpy, s->screen_num), &s->xft_fg_color);
718 XftColorFree (dpy, DefaultVisual (dpy, s->screen_num),
719 DefaultColormap (dpy, s->screen_num), &s->xft_bg_color);
720 XftFontClose (dpy, s->xft_font);
721 }
722#endif
723
724 XFreeCursor (dpy, s->rat);
725 XFreeColormap (dpy, s->def_cmap);
726 XFreeGC (dpy, s->normal_gc);
727 XFreeGC (dpy, s->inverse_gc);
728
729 free (s->display_string);
730 free (s->xrandr.name);
731}
732
733void
734screen_free_final (void)
735{
736 /* Relinquish our hold on the root window. */
737 XSelectInput(dpy, RootWindow (dpy, DefaultScreen (dpy)), 0);
738
739 numset_free (rp_glob_screen.numset);
740}