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(&copy, "@");
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