1 /* -*-c-*- */
2 /* This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, see: <http://www.gnu.org/licenses/>
14 */
15
16 #include "config.h"
17
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <stdbool.h>
21 #include <ctype.h>
22 #include <errno.h>
23 #include <err.h>
24
25 #include "defaults.h"
26 #include "fvwmlib.h"
27 #include "Parse.h"
28 #include "PictureBase.h"
29 #include "FScreen.h"
30 #include "FEvent.h"
31
32 #define GLOBAL_SCREEN_NAME "_global"
33
34 /* In fact, only corners matter -- there will never be GRAV_NONE */
35 enum {GRAV_POS = 0, GRAV_NONE = 1, GRAV_NEG = 2};
36 static int grav_matrix[3][3] =
37 {
38 { NorthWestGravity, NorthGravity, NorthEastGravity },
39 { WestGravity, CenterGravity, EastGravity },
40 { SouthWestGravity, SouthGravity, SouthEastGravity }
41 };
42 #define DEFAULT_GRAVITY NorthWestGravity
43
44 static Display *disp;
45 static bool is_randr_present;
46
47 static struct monitor *monitor_new(void);
48 static void scan_screens(Display *);
49 static void monitor_check_primary(void);
50 static void monitor_refresh_global(void);
51 static struct monitor *monitor_by_name(const char *);
52 static void monitor_scan_edges(struct monitor *);
53
54 enum monitor_tracking monitor_mode;
55 bool is_tracking_shared;
56 struct screen_infos screen_info_q;
57 struct monitors monitor_q;
58 int randr_event;
59 const char *prev_focused_monitor;
60 static struct monitor *monitor_global;
61
GetMouseXY(XEvent * eventp,int * x,int * y)62 static void GetMouseXY(XEvent *eventp, int *x, int *y)
63 {
64 XEvent e;
65
66 if (eventp == NULL)
67 {
68 eventp = &e;
69 e.type = 0;
70 }
71 fev_get_evpos_or_query(
72 disp, DefaultRootWindow(disp), eventp, x, y);
73 }
74
75 struct monitor *
monitor_new(void)76 monitor_new(void)
77 {
78 struct monitor *m;
79
80 m = fxcalloc(1, sizeof *m);
81
82 return (m);
83 }
84
85 static void
monitor_scan_edges(struct monitor * m)86 monitor_scan_edges(struct monitor *m)
87 {
88 struct monitor *m_loop;
89
90 m->edge.top = m->edge.bottom = m->edge.left = m->edge.right =
91 MONITOR_OUTSIDE_EDGE;
92
93 TAILQ_FOREACH(m_loop, &monitor_q, entry) {
94 if (m_loop == m)
95 continue;
96
97 /* Top Edge */
98 if ( m->si->y == m_loop->si->y + m_loop->si->h )
99 m->edge.top = MONITOR_INSIDE_EDGE;
100 /* Bottom Edge */
101 if ( m->si->y + m->si->h == m_loop->si->y )
102 m->edge.bottom = MONITOR_INSIDE_EDGE;
103 /* Left Edge */
104 if ( m->si->x == m_loop->si->x + m_loop->si->w )
105 m->edge.left = MONITOR_INSIDE_EDGE;
106 /* Right Edge */
107 if ( m->si->x + m->si->w == m_loop->si->x )
108 m->edge.right = MONITOR_INSIDE_EDGE;
109 }
110 }
111
112 void
monitor_refresh_global(void)113 monitor_refresh_global(void)
114 {
115 /* Creating the global monitor outside of the monitor_q structure
116 * allows for only the relevant information to be presented to callers
117 * which are asking for a global screen.
118 *
119 * This structure therefore only uses some of the fields:
120 *
121 * si->name,
122 * si->{x, y, w, h}
123 */
124 if (monitor_global == NULL) {
125 monitor_global = monitor_new();
126 monitor_global->si = screen_info_new();
127 monitor_global->si->name = fxstrdup(GLOBAL_SCREEN_NAME);
128 }
129
130 /* At this point, the global screen has been initialised. Refresh the
131 * coordinate list.
132 */
133 monitor_global->si->x = 0;
134 monitor_global->si->y = 0;
135 monitor_global->si->w = monitor_get_all_widths();
136 monitor_global->si->h = monitor_get_all_heights();
137 }
138
139 struct screen_info *
screen_info_new(void)140 screen_info_new(void)
141 {
142 struct screen_info *si;
143
144 si = fxcalloc(1, sizeof *si);
145
146 return (si);
147 }
148
149 struct screen_info *
screen_info_by_name(const char * name)150 screen_info_by_name(const char *name)
151 {
152 struct screen_info *si;
153
154 TAILQ_FOREACH(si, &screen_info_q, entry) {
155 if (strcmp(si->name, name) == 0)
156 return (si);
157 }
158
159 return (NULL);
160 }
161
162 struct monitor *
monitor_get_current(void)163 monitor_get_current(void)
164 {
165 int JunkX = 0, JunkY = 0, x, y;
166 Window JunkRoot, JunkChild;
167 unsigned int JunkMask;
168 struct monitor *mon;
169 static const char *cur_mon;
170
171 prev_focused_monitor = cur_mon;
172
173 FQueryPointer(disp, DefaultRootWindow(disp), &JunkRoot, &JunkChild,
174 &JunkX, &JunkY, &x, &y, &JunkMask);
175
176 mon = FindScreenOfXY(x, y);
177
178 if (mon != NULL)
179 cur_mon = mon->si->name;
180
181 return (mon);
182 }
183
184 struct monitor *
monitor_resolve_name(const char * scr)185 monitor_resolve_name(const char *scr)
186 {
187 struct monitor *m = NULL;
188
189 /* Assume the monitor name is a literal RandR name (such as HDMI2) and
190 * look it up regardless.
191 */
192 m = monitor_by_name(scr);
193
194 /* If we've asked for "@g" then use the global screen. The
195 * x,y,w,h values are already assigned, so skip that.
196 */
197 if (strcmp(scr, "g") == 0) {
198 monitor_refresh_global();
199 m = monitor_global;
200 }
201
202 /* "@c" is for the current screen. */
203 if (strcmp(scr, "c") == 0)
204 m = monitor_get_current();
205
206 /* "@p" is for the primary screen. */
207 if (strcmp(scr, "p") == 0)
208 m = monitor_by_primary();
209
210 if (m == NULL) {
211 /* Should not happen. */
212 fvwm_debug(__func__, "no monitor found with name '%s'", scr);
213 return (TAILQ_FIRST(&monitor_q));
214 }
215
216 return (m);
217 }
218
219 static struct monitor *
monitor_by_name(const char * name)220 monitor_by_name(const char *name)
221 {
222 struct monitor *m, *mret = NULL;
223
224 if (name == NULL) {
225 fvwm_debug(__func__, "%s: name is NULL; shouldn't happen. "
226 "Returning current monitor\n", __func__);
227 return (monitor_get_current());
228 }
229
230 TAILQ_FOREACH(m, &monitor_q, entry) {
231 if (strcmp(m->si->name, name) == 0) {
232 mret = m;
233 break;
234 }
235 }
236
237 if (mret == NULL && (strcmp(name, GLOBAL_SCREEN_NAME) == 0)) {
238 if (monitor_get_count() == 1) {
239 /* In this case, the global screen was requested, but
240 * we've only one monitor in use. Return this monitor
241 * instead.
242 */
243 return (TAILQ_FIRST(&monitor_q));
244 } else {
245 /* Return the current monitor. */
246 mret = monitor_get_current();
247 }
248 }
249
250 /* Then we couldn't find the named monitor at all. Return the current
251 * monitor instead.
252 */
253
254 if (mret == NULL) {
255 mret = monitor_get_current();
256 if (mret == NULL)
257 return (NULL);
258 }
259
260 return (mret);
261 }
262
263 struct monitor *
monitor_by_output(int output)264 monitor_by_output(int output)
265 {
266 struct monitor *m, *mret = NULL;
267
268 TAILQ_FOREACH(m, &monitor_q, entry) {
269 if (m->si->rr_output == output) {
270 mret = m;
271 break;
272 }
273 }
274
275 if (mret == NULL) {
276 fvwm_debug(__func__,
277 "%s: couldn't find monitor with output '%d', "
278 "returning first output.\n", __func__, output);
279 mret = TAILQ_FIRST(&monitor_q);
280 }
281
282 return (mret);
283 }
284
285 struct monitor *
monitor_by_primary(void)286 monitor_by_primary(void)
287 {
288 struct monitor *m = NULL, *m_loop;
289
290 TAILQ_FOREACH(m_loop, &monitor_q, entry) {
291 if (m_loop->flags & MONITOR_PRIMARY) {
292 m = m_loop;
293 break;
294 }
295 }
296
297 return (m);
298 }
299
300 static void
monitor_check_primary(void)301 monitor_check_primary(void)
302 {
303 if (monitor_by_primary() == NULL) {
304 struct monitor *m;
305
306 m = TAILQ_FIRST(&monitor_q);
307 m->flags |= MONITOR_PRIMARY;
308 }
309 }
310
311 int
monitor_get_all_widths(void)312 monitor_get_all_widths(void)
313 {
314 return (DisplayWidth(disp, DefaultScreen(disp)));
315 }
316
317 int
monitor_get_all_heights(void)318 monitor_get_all_heights(void)
319 {
320 return (DisplayHeight(disp, DefaultScreen(disp)));
321 }
322
323 void
monitor_assign_virtual(struct monitor * ref)324 monitor_assign_virtual(struct monitor *ref)
325 {
326 struct monitor *m;
327
328 if (monitor_mode == MONITOR_TRACKING_M || is_tracking_shared)
329 return;
330
331 TAILQ_FOREACH(m, &monitor_q, entry) {
332 if (m == ref)
333 continue;
334
335 memcpy(&m->virtual_scr, &ref->virtual_scr, sizeof(m->virtual_scr));
336 }
337 }
338
339
340 void
FScreenSelect(Display * dpy)341 FScreenSelect(Display *dpy)
342 {
343 XRRSelectInput(disp, DefaultRootWindow(disp),
344 RRScreenChangeNotifyMask | RROutputChangeNotifyMask);
345 }
346
347 void
monitor_output_change(Display * dpy,XRRScreenChangeNotifyEvent * e)348 monitor_output_change(Display *dpy, XRRScreenChangeNotifyEvent *e)
349 {
350 XRRScreenResources *res;
351 struct monitor *m = NULL;
352
353 fvwm_debug(__func__, "%s: outputs have changed\n", __func__);
354
355 if ((res = XRRGetScreenResources(dpy, e->root)) == NULL) {
356 fvwm_debug(__func__,
357 "%s: couldn't acquire screen resources\n",
358 __func__);
359 return;
360 }
361
362 {
363 struct screen_info *si;
364 XRROutputInfo *oinfo = NULL;
365 int i;
366
367 scan_screens(dpy);
368
369 for (i = 0; i < res->noutput; i++) {
370 oinfo = XRRGetOutputInfo(dpy, res, res->outputs[i]);
371 if (oinfo == NULL)
372 continue;
373
374 si = screen_info_by_name(oinfo->name);
375
376 if (si == NULL)
377 continue;
378
379 TAILQ_FOREACH(m, &monitor_q, entry) {
380 if (m->si == si) {
381 m->emit &= ~MONITOR_ALL;
382 break;
383 }
384 }
385
386 if (m == NULL)
387 continue;
388
389 if (oinfo->connection == RR_Connected &&
390 m->flags & MONITOR_ENABLED) {
391 if (m->flags & MONITOR_CHANGED)
392 m->emit |= MONITOR_CHANGED;
393 continue;
394 }
395
396 if (oinfo->connection == RR_Disconnected &&
397 m->flags & MONITOR_DISABLED) {
398 continue;
399 }
400
401 switch (oinfo->connection) {
402 case RR_Connected:
403 m->flags &= ~MONITOR_DISABLED;
404 m->flags |= MONITOR_ENABLED;
405
406 m->emit |= MONITOR_ENABLED;
407 break;
408 case RR_Disconnected:
409 m->flags &= ~MONITOR_ENABLED;
410 m->flags |= MONITOR_DISABLED;
411 m->emit |= MONITOR_DISABLED;
412 break;
413 default:
414 break;
415 }
416 }
417 XRRFreeOutputInfo(oinfo);
418 }
419 XRRFreeScreenResources(res);
420
421 TAILQ_FOREACH(m, &monitor_q, entry)
422 monitor_scan_edges(m);
423
424 monitor_check_primary();
425 }
426
427 static void
scan_screens(Display * dpy)428 scan_screens(Display *dpy)
429 {
430 XRRMonitorInfo *rrm;
431 struct monitor *m;
432 int i, n = 0;
433 Window root = RootWindow(dpy, DefaultScreen(dpy));
434
435 rrm = XRRGetMonitors(dpy, root, false, &n);
436 if (n <= 0) {
437 fvwm_debug(__func__, "get monitors failed\n");
438 exit(101);
439 }
440
441 for (i = 0; i < n; i++) {
442
443 char *name = XGetAtomName(dpy, rrm[i].name);
444
445 if (name == NULL) {
446 fprintf(stderr, "%s: couldn't detect monitor with empty name\n", __func__);
447 exit (101);
448 }
449
450 if (((m = monitor_by_name(name)) == NULL) ||
451 (m != NULL && strcmp(m->si->name, name) != 0)) {
452 m = monitor_new();
453 m->flags |= MONITOR_NEW;
454 m->si = screen_info_new();
455 m->si->name = strdup(name);
456 memset(&m->virtual_scr, 0, sizeof(m->virtual_scr));
457
458 TAILQ_INSERT_TAIL(&screen_info_q, m->si, entry);
459 TAILQ_INSERT_TAIL(&monitor_q, m, entry);
460
461 goto set_coords;
462 }
463
464 if ((strcmp(m->si->name, name) == 0) &&
465 (m->si->x != rrm[i].x || m->si->y != rrm[i].y ||
466 m->si->w != rrm[i].width || m->si->h != rrm[i].height)) {
467 if (m->flags & MONITOR_ENABLED)
468 m->flags |= MONITOR_CHANGED;
469 }
470
471 set_coords:
472 m->si->x = rrm[i].x;
473 m->si->y = rrm[i].y;
474 m->si->w = rrm[i].width;
475 m->si->h = rrm[i].height;
476 m->si->rr_output = *rrm[i].outputs;
477 if (rrm[i].primary > 0)
478 m->flags |= MONITOR_PRIMARY;
479 else
480 m->flags &= ~MONITOR_PRIMARY;
481
482 XFree(name);
483 }
484
485 monitor_scan_edges(m);
486 monitor_check_primary();
487 XRRFreeMonitors(rrm);
488 }
489
FScreenInit(Display * dpy)490 void FScreenInit(Display *dpy)
491 {
492 XRRScreenResources *res = NULL;
493 struct monitor *m;
494 int err_base = 0, major, minor;
495
496 disp = dpy;
497 randr_event = 0;
498 is_randr_present = false;
499
500 if (TAILQ_EMPTY(&monitor_q))
501 TAILQ_INIT(&monitor_q);
502
503 if (TAILQ_EMPTY(&screen_info_q))
504 TAILQ_INIT(&screen_info_q);
505
506 if (!XRRQueryExtension(dpy, &randr_event, &err_base) ||
507 !XRRQueryVersion (dpy, &major, &minor)) {
508 fvwm_debug(__func__, "RandR not present");
509 goto randr_fail;
510 }
511
512 if (major == 1 && minor >= 5)
513 is_randr_present = true;
514
515
516 if (!is_randr_present) {
517 /* Something went wrong. */
518 fvwm_debug(__func__, "Couldn't initialise XRandR: %s\n",
519 strerror(errno));
520 goto randr_fail;
521 }
522
523 fvwm_debug(__func__, "Using RandR %d.%d\n", major, minor);
524
525 /* XRandR is present, so query the screens we have. */
526 res = XRRGetScreenResources(dpy, DefaultRootWindow(dpy));
527
528 if (res == NULL || (res != NULL && res->noutput == 0)) {
529 XRRFreeScreenResources(res);
530 fvwm_debug(__func__, "RandR present, yet no outputs found.");
531 goto randr_fail;
532 }
533 XRRFreeScreenResources(res);
534
535 scan_screens(dpy);
536 is_tracking_shared = false;
537
538 TAILQ_FOREACH(m, &monitor_q, entry) {
539 m->Desktops = fxcalloc(1, sizeof *m->Desktops);
540 m->Desktops->name = NULL;
541 m->Desktops->next = NULL;
542 m->Desktops->desk = 0;
543 m->flags |= (MONITOR_NEW|MONITOR_ENABLED);
544 monitor_scan_edges(m);
545 }
546
547 monitor_check_primary();
548
549 return;
550
551 randr_fail:
552 fprintf(stderr, "Unable to initialise RandR\n");
553 exit(101);
554 }
555
556 void
monitor_dump_state(struct monitor * m)557 monitor_dump_state(struct monitor *m)
558 {
559 struct monitor *mcur, *m2;
560
561 mcur = monitor_get_current();
562
563 fvwm_debug(__func__, "Monitor Debug\n");
564 fvwm_debug(__func__, "\tnumber of outputs: %d\n", monitor_get_count());
565 TAILQ_FOREACH(m2, &monitor_q, entry) {
566 if (m2 == NULL) {
567 fvwm_debug(__func__,
568 "monitor in list is NULL. Bug!\n");
569 continue;
570 }
571 if (m != NULL && m2 != m)
572 continue;
573 fvwm_debug(__func__,
574 "\tName:\t%s\n"
575 "\tDisabled:\t%s\n"
576 "\tIs Primary:\t%s\n"
577 "\tIs Current:\t%s\n"
578 "\tOutput:\t%d\n"
579 "\tCoords:\t{x: %d, y: %d, w: %d, h: %d}\n"
580 "\tVirtScr: {\n"
581 "\t\tVxMax: %d, VyMax: %d, Vx: %d, Vy: %d\n"
582 "\t\tEdgeScrollX: %d, EdgeScrollY: %d\n"
583 "\t\tCurrentDesk: %d\n"
584 "\t\tCurrentPage: {x: %d, y: %d}\n"
585 "\t\tMyDisplayWidth: %d, MyDisplayHeight: %d\n\t}\n"
586 "\tDesktops:\t%s\n"
587 "\tFlags:%s\n\n",
588 m2->si->name,
589 (m2->flags & MONITOR_DISABLED) ? "true" : "false",
590 (m2->flags & MONITOR_PRIMARY) ? "yes" : "no",
591 (mcur && m2 == mcur) ? "yes" : "no",
592 (int)m2->si->rr_output,
593 m2->si->x, m2->si->y, m2->si->w, m2->si->h,
594 m2->virtual_scr.VxMax, m2->virtual_scr.VyMax,
595 m2->virtual_scr.Vx, m2->virtual_scr.Vy,
596 m2->virtual_scr.EdgeScrollX,
597 m2->virtual_scr.EdgeScrollY,
598 m2->virtual_scr.CurrentDesk,
599 (int)(m2->virtual_scr.Vx / monitor_get_all_widths()),
600 (int)(m2->virtual_scr.Vy / monitor_get_all_heights()),
601 monitor_get_all_widths(),
602 monitor_get_all_heights(),
603 m2->Desktops ? "yes" : "no",
604 monitor_mode == MONITOR_TRACKING_G ? "global" :
605 monitor_mode == MONITOR_TRACKING_M ? "per-monitor" :
606 "Unknown"
607 );
608 }
609 }
610
611 int
monitor_get_count(void)612 monitor_get_count(void)
613 {
614 struct monitor *m = NULL;
615 int c = 0;
616
617 TAILQ_FOREACH(m, &monitor_q, entry) {
618 if (m->flags & MONITOR_DISABLED)
619 continue;
620 c++;
621 }
622 return (c);
623 }
624
625 struct monitor *
FindScreenOfXY(int x,int y)626 FindScreenOfXY(int x, int y)
627 {
628 struct monitor *m;
629 int xa, ya;
630 int all_widths, all_heights;
631
632 all_widths = monitor_get_all_widths();
633 all_heights = monitor_get_all_heights();
634
635 xa = abs(x);
636 ya = abs(y);
637
638 xa %= all_widths;
639 ya %= all_heights;
640
641 TAILQ_FOREACH(m, &monitor_q, entry) {
642 /* If we have more than one screen configured, then don't match
643 * on the global screen, as that's separate to XY positioning
644 * which is only concerned with the *specific* screen.
645 */
646 if (monitor_get_count() > 0 &&
647 strcmp(m->si->name, GLOBAL_SCREEN_NAME) == 0)
648 continue;
649 if (xa >= m->si->x && xa < m->si->x + m->si->w &&
650 ya >= m->si->y && ya < m->si->y + m->si->h)
651 return (m);
652 }
653
654 /* FIXME: this is a convenience, but could confuse callers. */
655 if (m == NULL)
656 return TAILQ_FIRST(&monitor_q);
657
658 return (NULL);
659 }
660
661 static struct monitor *
FindScreen(fscreen_scr_arg * arg,fscreen_scr_t screen)662 FindScreen(fscreen_scr_arg *arg, fscreen_scr_t screen)
663 {
664 struct monitor *m = NULL;
665 fscreen_scr_arg tmp;
666
667 if (monitor_get_count() == 0) {
668 monitor_refresh_global();
669 return (monitor_global);
670 }
671
672 switch (screen)
673 {
674 case FSCREEN_GLOBAL:
675 monitor_refresh_global();
676 m = monitor_global;
677 break;
678 case FSCREEN_PRIMARY:
679 m = monitor_by_primary();
680 break;
681 case FSCREEN_CURRENT:
682 /* translate to xypos format */
683 if (!arg)
684 {
685 tmp.mouse_ev = NULL;
686 arg = &tmp;
687 }
688 GetMouseXY(arg->mouse_ev, &arg->xypos.x, &arg->xypos.y);
689 /* fall through */
690 case FSCREEN_XYPOS:
691 /* translate to screen number */
692 if (!arg)
693 {
694 tmp.xypos.x = 0;
695 tmp.xypos.y = 0;
696 arg = &tmp;
697 }
698 m = FindScreenOfXY(arg->xypos.x, arg->xypos.y);
699 break;
700 case FSCREEN_BY_NAME:
701 if (arg == NULL || arg->name == NULL) {
702 /* XXX: Work out what to do. */
703 break;
704 }
705 m = monitor_resolve_name(arg->name);
706 break;
707 default:
708 /* XXX: Possible error condition here? */
709 break;
710 }
711
712 return (m);
713 }
714
715 /* Given pointer coordinates, return the screen the pointer is on.
716 *
717 * Perhaps most useful with $[pointer.screen]
718 */
719 const char *
FScreenOfPointerXY(int x,int y)720 FScreenOfPointerXY(int x, int y)
721 {
722 struct monitor *m;
723
724 m = FindScreenOfXY(x, y);
725
726 return (m != NULL) ? m->si->name : "unknown";
727 }
728
729 /* Returns the specified screens geometry rectangle. screen can be a screen
730 * number or any of the values FSCREEN_GLOBAL, FSCREEN_CURRENT,
731 * FSCREEN_PRIMARY or FSCREEN_XYPOS. The arg union only has a meaning for
732 * FSCREEN_CURRENT and FSCREEN_XYARG. For FSCREEN_CURRENT its mouse_ev member
733 * may be given. It is tried to find out the pointer position from the event
734 * first before querying the pointer. For FSCREEN_XYPOS the xpos member is used
735 * to fetch the x/y position of the point on the screen. If arg is NULL, the
736 * position 0 0 is assumed instead.
737 *
738 * Any of the arguments arg, x, y, w and h may be NULL.
739 *
740 * FSCREEN_GLOBAL: return the global screen dimensions
741 * FSCREEN_CURRENT: return dimensions of the screen with the pointer
742 * FSCREEN_PRIMARY: return the primary screen dimensions
743 * FSCREEN_XYPOS: return dimensions of the screen with the given coordinates
744 * FSCREEN_BY_NAME: return dimensions of the screen with the given name
745 *
746 * The function returns False if the global screen was returned and more than
747 * one screen is configured. Otherwise it returns True.
748 */
FScreenGetScrRect(fscreen_scr_arg * arg,fscreen_scr_t screen,int * x,int * y,int * w,int * h)749 Bool FScreenGetScrRect(fscreen_scr_arg *arg, fscreen_scr_t screen,
750 int *x, int *y, int *w, int *h)
751 {
752 struct monitor *m = FindScreen(arg, screen);
753 if (m == NULL) {
754 fvwm_debug(__func__, "%s: m is NULL\n", __func__);
755 return (True);
756 }
757
758 if (x)
759 *x = m->si->x;
760 if (y)
761 *y = m->si->y;
762 if (w)
763 *w = m->si->w;
764 if (h)
765 *h = m->si->h;
766
767 return !((monitor_get_count() > 1) &&
768 (strcmp(m->si->name, GLOBAL_SCREEN_NAME) == 0));
769 }
770
771 /* Translates the coodinates *x *y from the screen specified by arg_src and
772 * screen_src to coordinates on the screen specified by arg_dest and
773 * screen_dest. (see FScreenGetScrRect for more details). */
FScreenTranslateCoordinates(fscreen_scr_arg * arg_src,fscreen_scr_t screen_src,fscreen_scr_arg * arg_dest,fscreen_scr_t screen_dest,int * x,int * y)774 void FScreenTranslateCoordinates(
775 fscreen_scr_arg *arg_src, fscreen_scr_t screen_src,
776 fscreen_scr_arg *arg_dest, fscreen_scr_t screen_dest,
777 int *x, int *y)
778 {
779 int x_src;
780 int y_src;
781 int x_dest;
782 int y_dest;
783
784 FScreenGetScrRect(arg_src, screen_src, &x_src, &y_src, NULL, NULL);
785 FScreenGetScrRect(arg_dest, screen_dest, &x_dest, &y_dest, NULL, NULL);
786
787 if (x)
788 {
789 *x = *x + x_src - x_dest;
790 }
791 if (y)
792 {
793 *y = *y + y_src - y_dest;
794 }
795
796 return;
797 }
798
799 /* Arguments work exactly like for FScreenGetScrRect() */
FScreenClipToScreen(fscreen_scr_arg * arg,fscreen_scr_t screen,int * x,int * y,int w,int h)800 int FScreenClipToScreen(fscreen_scr_arg *arg, fscreen_scr_t screen,
801 int *x, int *y, int w, int h)
802 {
803 int sx;
804 int sy;
805 int sw;
806 int sh;
807 int lx = (x) ? *x : 0;
808 int ly = (y) ? *y : 0;
809 int x_grav = GRAV_POS;
810 int y_grav = GRAV_POS;
811
812 FScreenGetScrRect(arg, screen, &sx, &sy, &sw, &sh);
813 if (lx + w > sx + sw)
814 {
815 lx = sx + sw - w;
816 x_grav = GRAV_NEG;
817 }
818 if (ly + h > sy + sh)
819 {
820 ly = sy + sh - h;
821 y_grav = GRAV_NEG;
822 }
823 if (lx < sx)
824 {
825 lx = sx;
826 x_grav = GRAV_POS;
827 }
828 if (ly < sy)
829 {
830 ly = sy;
831 y_grav = GRAV_POS;
832 }
833 if (x)
834 {
835 *x = lx;
836 }
837 if (y)
838 {
839 *y = ly;
840 }
841
842 return grav_matrix[y_grav][x_grav];
843 }
844
845 /* Arguments work exactly like for FScreenGetScrRect() */
FScreenCenterOnScreen(fscreen_scr_arg * arg,fscreen_scr_t screen,int * x,int * y,int w,int h)846 void FScreenCenterOnScreen(fscreen_scr_arg *arg, fscreen_scr_t screen,
847 int *x, int *y, int w, int h)
848 {
849 int sx;
850 int sy;
851 int sw;
852 int sh;
853 int lx;
854 int ly;
855
856 FScreenGetScrRect(arg, screen, &sx, &sy, &sw, &sh);
857 lx = (sw - w) / 2;
858 ly = (sh - h) / 2;
859 if (lx < 0)
860 lx = 0;
861 if (ly < 0)
862 ly = 0;
863 lx += sx;
864 ly += sy;
865 if (x)
866 {
867 *x = lx;
868 }
869 if (y)
870 {
871 *y = ly;
872 }
873 }
874
FScreenGetResistanceRect(int wx,int wy,unsigned int ww,unsigned int wh,int * x0,int * y0,int * x1,int * y1)875 void FScreenGetResistanceRect(
876 int wx, int wy, unsigned int ww, unsigned int wh, int *x0, int *y0,
877 int *x1, int *y1)
878 {
879 fscreen_scr_arg arg;
880
881 arg.xypos.x = wx + ww / 2;
882 arg.xypos.y = wy + wh / 2;
883 FScreenGetScrRect(&arg, FSCREEN_XYPOS, x0, y0, x1, y1);
884 *x1 += *x0;
885 *y1 += *y0;
886 }
887
888 /* Arguments work exactly like for FScreenGetScrRect() */
FScreenIsRectangleOnScreen(fscreen_scr_arg * arg,fscreen_scr_t screen,rectangle * rec)889 Bool FScreenIsRectangleOnScreen(fscreen_scr_arg *arg, fscreen_scr_t screen,
890 rectangle *rec)
891 {
892 int sx;
893 int sy;
894 int sw;
895 int sh;
896
897 FScreenGetScrRect(arg, screen, &sx, &sy, &sw, &sh);
898
899 return (rec->x + rec->width > sx && rec->x < sx + sw &&
900 rec->y + rec->height > sy && rec->y < sy + sh) ? True : False;
901 }
902
903 /*
904 * FScreenParseGeometry
905 * Does the same as XParseGeometry, but handles additional "@scr".
906 * Since it isn't safe to define "ScreenValue" constant (actual values
907 * of other "XXXValue" are specified in Xutil.h, not by us, so there can
908 * be a clash), the screen value is always returned, even if it wasn't
909 * present in `parse_string' (set to default in that case).
910 *
911 */
FScreenParseGeometryWithScreen(char * parsestring,int * x_return,int * y_return,unsigned int * width_return,unsigned int * height_return,char ** screen_return)912 int FScreenParseGeometryWithScreen(
913 char *parsestring, int *x_return, int *y_return,
914 unsigned int *width_return, unsigned int *height_return,
915 char **screen_return)
916 {
917 char *copy, *geom_str = NULL;
918 int ret;
919
920 /* Safety net */
921 if (parsestring == NULL || *parsestring == '\0') {
922 *screen_return = NULL;
923 return 0;
924 }
925
926 /* No screen specified; parse geometry as standard. */
927 if (strchr(parsestring, '@') == NULL) {
928 *screen_return = NULL;
929 copy = fxstrdup(parsestring);
930 goto parse_geometry;
931 }
932
933 /* If the geometry specification contains an '@' symbol, assume the
934 * screen is specified. This must be the name of the monitor in
935 * question!
936 */
937 copy = fxstrdup(parsestring);
938 copy = strsep(&parsestring, "@");
939
940 *screen_return = fxstrdup(parsestring);
941 geom_str = strsep(©, "@");
942 copy = geom_str;
943
944 parse_geometry:
945 /* Do the parsing */
946 ret = XParseGeometry(
947 copy, x_return, y_return, width_return, height_return);
948
949 if (*screen_return == NULL)
950 *screen_return = fxstrdup(monitor_by_primary()->si->name);
951
952 return ret;
953 }
954
955 /* Same as above, but dump screen return value to keep compatible with the X
956 * function. */
FScreenParseGeometry(char * parsestring,int * x_return,int * y_return,unsigned int * width_return,unsigned int * height_return)957 int FScreenParseGeometry(
958 char *parsestring, int *x_return, int *y_return,
959 unsigned int *width_return, unsigned int *height_return)
960 {
961 struct monitor *m = monitor_get_current();
962 char *scr = NULL;
963 int rc, x, y, w, h;
964
965 x = 0;
966 y = 0;
967 w = monitor_get_all_widths();
968 h = monitor_get_all_heights();
969
970 rc = FScreenParseGeometryWithScreen(
971 parsestring, x_return, y_return, width_return, height_return,
972 &scr);
973
974 if (scr != NULL) {
975 m = monitor_resolve_name(scr);
976 fprintf(stderr, "Found monitor with name of: %s (%s)\n", scr, m->si->name);
977 x = m->si->x;
978 y = m->si->y;
979 w = m->si->w;
980 h = m->si->h;
981 }
982 free(scr);
983
984 /* adapt geometry to selected screen */
985 if (rc & XValue)
986 {
987 if (rc & XNegative)
988 *x_return -= (monitor_get_all_widths() - w - x);
989 else
990 *x_return += x;
991 }
992 if (rc & YValue)
993 {
994 if (rc & YNegative)
995 *y_return -= (monitor_get_all_heights() - h - y);
996 else
997 *y_return += y;
998 }
999 return rc;
1000 }
1001
1002
1003 /* FScreenGetGeometry
1004 * Parses the geometry in a form: XGeometry[@screen], i.e.
1005 * [=][<width>{xX}<height>][{+-}<xoffset>{+-}<yoffset>][@<screen>]
1006 * where <screen> is either a number or "G" (global) "C" (current)
1007 * or "P" (primary)
1008 *
1009 * Args:
1010 * parsestring, x_r, y_r, w_r, h_r the same as in XParseGeometry
1011 * hints window hints structure, may be NULL
1012 * flags bitmask of allowed flags (XValue, WidthValue, XNegative...)
1013 *
1014 * Note1:
1015 * hints->width and hints->height will be used to calc negative geometry
1016 * if width/height isn't specified in the geometry itself.
1017 *
1018 * Note2:
1019 * This function's behaviour is crafted to sutisfy/emulate the
1020 * FvwmWinList::MakeMeWindow()'s behaviour.
1021 *
1022 * Note3:
1023 * A special value of `flags' when [XY]Value are there but [XY]Negative
1024 * aren't, means that in case of negative geometry specification
1025 * x_r/y_r values will be promoted to the screen border, but w/h
1026 * wouldn't be subtracted, so that the program can do x-=w later
1027 * ([XY]Negative *will* be returned, albeit absent in `flags').
1028 * This option is supposed for proggies like FvwmButtons, which
1029 * receive geometry specification long before they are able to actually
1030 * use it (and which calculate w/h themselves).
1031 * (The same effect can't be obtained with omitting {Width,Height}Value
1032 * in the flags, since the app may wish to get the dimensions but apply
1033 * some constraints later (as FvwmButtons do, BTW...).)
1034 * This option can be also useful in cases where dimensions are
1035 * specified not in pixels but in some other units (e.g., charcells).
1036 */
FScreenGetGeometry(char * parsestring,int * x_return,int * y_return,int * width_return,int * height_return,XSizeHints * hints,int flags)1037 int FScreenGetGeometry(
1038 char *parsestring, int *x_return, int *y_return,
1039 int *width_return, int *height_return, XSizeHints *hints, int flags)
1040 {
1041 fscreen_scr_arg arg;
1042 char *scr = NULL;
1043 int ret;
1044 int saved;
1045 int x, y;
1046 unsigned int w = 0, h = 0;
1047 int grav, x_grav, y_grav;
1048 int scr_x, scr_y;
1049 int scr_w, scr_h;
1050
1051 /* I. Do the parsing and strip off extra bits */
1052 ret = FScreenParseGeometryWithScreen(parsestring, &x, &y, &w, &h, &scr);
1053 saved = ret & (XNegative | YNegative);
1054 ret &= flags;
1055
1056 arg.mouse_ev = NULL;
1057 arg.name = scr;
1058 FScreenGetScrRect(&arg, FSCREEN_BY_NAME, &scr_x, &scr_y, &scr_w, &scr_h);
1059
1060 /* II. Interpret and fill in the values */
1061
1062 /* Fill in dimensions for future negative calculations if
1063 * omitted/forbidden */
1064 /* Maybe should use *x_return,*y_return if hints==NULL?
1065 * Unreliable... */
1066 if (hints != NULL && hints->flags & PSize)
1067 {
1068 if ((ret & WidthValue) == 0)
1069 {
1070 w = hints->width;
1071 }
1072 if ((ret & HeightValue) == 0)
1073 {
1074 h = hints->height;
1075 }
1076 }
1077 else
1078 {
1079 /* This branch is required for case when size *is* specified,
1080 * but masked off */
1081 if ((ret & WidthValue) == 0)
1082 {
1083 w = 0;
1084 }
1085 if ((ret & HeightValue) == 0)
1086 {
1087 h = 0;
1088 }
1089 }
1090
1091 /* Advance coords to the screen... */
1092 x += scr_x;
1093 y += scr_y;
1094
1095 /* ...and process negative geometries */
1096 if (saved & XNegative)
1097 {
1098 x += scr_w;
1099 }
1100 if (saved & YNegative)
1101 {
1102 y += scr_h;
1103 }
1104 if (ret & XNegative)
1105 {
1106 x -= w;
1107 }
1108 if (ret & YNegative)
1109 {
1110 y -= h;
1111 }
1112
1113 /* Restore negative bits */
1114 ret |= saved;
1115
1116 /* Guess orientation */
1117 x_grav = (ret & XNegative)? GRAV_NEG : GRAV_POS;
1118 y_grav = (ret & YNegative)? GRAV_NEG : GRAV_POS;
1119 grav = grav_matrix[y_grav][x_grav];
1120
1121 /* Return the values */
1122 if (ret & XValue)
1123 {
1124 *x_return = x;
1125 if (hints != NULL)
1126 {
1127 hints->x = x;
1128 }
1129 }
1130 if (ret & YValue)
1131 {
1132 *y_return = y;
1133 if (hints != NULL)
1134 {
1135 hints->y = y;
1136 }
1137 }
1138 if (ret & WidthValue)
1139 {
1140 *width_return = w;
1141 if (hints != NULL)
1142 {
1143 hints->width = w;
1144 }
1145 }
1146 if (ret & HeightValue)
1147 {
1148 *height_return = h;
1149 if (hints != NULL)
1150 {
1151 hints->height = h;
1152 }
1153 }
1154 if (1 /*flags & GravityValue*/ && grav != DEFAULT_GRAVITY)
1155 {
1156 if (hints != NULL && hints->flags & PWinGravity)
1157 {
1158 hints->win_gravity = grav;
1159 }
1160 }
1161 if (hints != NULL && ret & XValue && ret & YValue)
1162 hints->flags |= USPosition;
1163
1164 return ret;
1165 }
1166
1167 /* FScreenMangleScreenIntoUSPosHints
1168 * A hack to mangle the screen number into the XSizeHints structure.
1169 * If the USPosition flag is set, hints->x is set to the magic number and
1170 * hints->y is set to the screen number. If the USPosition flag is clear,
1171 * x and y are set to zero.
1172 *
1173 * Note: This is a *hack* to allow modules to specify the target screen for
1174 * their windows and have the StartsOnScreen style set for them at the same
1175 * time. Do *not* rely on the mechanism described above.
1176 */
FScreenMangleScreenIntoUSPosHints(fscreen_scr_t screen,XSizeHints * hints)1177 void FScreenMangleScreenIntoUSPosHints(fscreen_scr_t screen, XSizeHints *hints)
1178 {
1179 if (hints->flags & USPosition)
1180 {
1181 hints->x = FSCREEN_MANGLE_USPOS_HINTS_MAGIC;
1182 hints->y = (short)screen;
1183 }
1184 else
1185 {
1186 hints->x = 0;
1187 hints->y = 0;
1188 }
1189
1190 return;
1191 }
1192
1193 /* FScreenMangleScreenIntoUSPosHints
1194 * A hack to mangle the screen number into the XSizeHints structure.
1195 * If the USPosition flag is set, hints->x is set to the magic number and
1196 * hints->y is set to the screen spec. If the USPosition flag is clear,
1197 * x and y are set to zero.
1198 *
1199 * Note: This is a *hack* to allow modules to specify the target screen for
1200 * their windows and have the StartsOnScreen style set for them at the same
1201 * time. Do *not* rely on the mechanism described above.
1202 */
FScreenFetchMangledScreenFromUSPosHints(XSizeHints * hints)1203 int FScreenFetchMangledScreenFromUSPosHints(XSizeHints *hints)
1204 {
1205 int screen;
1206
1207 if ((hints->flags & USPosition) &&
1208 hints->x == FSCREEN_MANGLE_USPOS_HINTS_MAGIC)
1209 {
1210 screen = hints->y;
1211 } else
1212 screen = 0;
1213
1214 return screen;
1215 }
1216