1 /*
2  *  Window Maker window manager
3  *
4  *  Copyright (c) 1997-2003 Alfredo K. Kojima
5  *
6  *  This program 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 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program 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 along
17  *  with this program; if not, write to the Free Software Foundation, Inc.,
18  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19  */
20 
21 #include <stdlib.h>
22 
23 #include "wconfig.h"
24 
25 #include "xinerama.h"
26 
27 #include "screen.h"
28 #include "window.h"
29 #include "framewin.h"
30 #include "placement.h"
31 #include "dock.h"
32 
33 #ifdef USE_XINERAMA
34 # ifdef SOLARIS_XINERAMA	/* sucks */
35 #  include <X11/extensions/xinerama.h>
36 # else
37 #  include <X11/extensions/Xinerama.h>
38 # endif
39 #endif
40 
wInitXinerama(WScreen * scr)41 void wInitXinerama(WScreen * scr)
42 {
43 	scr->xine_info.primary_head = 0;
44 	scr->xine_info.screens = NULL;
45 	scr->xine_info.count = 0;
46 #ifdef USE_XINERAMA
47 # ifdef SOLARIS_XINERAMA
48 	if (XineramaGetState(dpy, scr->screen)) {
49 		WXineramaInfo *info = &scr->xine_info;
50 		XRectangle head[MAXFRAMEBUFFERS];
51 		unsigned char hints[MAXFRAMEBUFFERS];
52 		int i;
53 
54 		if (XineramaGetInfo(dpy, scr->screen, head, hints, &info->count)) {
55 
56 			info->screens = wmalloc(sizeof(WMRect) * (info->count + 1));
57 
58 			for (i = 0; i < info->count; i++) {
59 				info->screens[i].pos.x = head[i].x;
60 				info->screens[i].pos.y = head[i].y;
61 				info->screens[i].size.width = head[i].width;
62 				info->screens[i].size.height = head[i].height;
63 			}
64 		}
65 	}
66 # else				/* !SOLARIS_XINERAMA */
67 	if (XineramaIsActive(dpy)) {
68 		XineramaScreenInfo *xine_screens;
69 		WXineramaInfo *info = &scr->xine_info;
70 		int i;
71 
72 		xine_screens = XineramaQueryScreens(dpy, &info->count);
73 
74 		info->screens = wmalloc(sizeof(WMRect) * (info->count + 1));
75 
76 		for (i = 0; i < info->count; i++) {
77 			info->screens[i].pos.x = xine_screens[i].x_org;
78 			info->screens[i].pos.y = xine_screens[i].y_org;
79 			info->screens[i].size.width = xine_screens[i].width;
80 			info->screens[i].size.height = xine_screens[i].height;
81 		}
82 		XFree(xine_screens);
83 	}
84 # endif				/* !SOLARIS_XINERAMA */
85 #endif				/* USE_XINERAMA */
86 }
87 
wGetRectPlacementInfo(WScreen * scr,WMRect rect,int * flags)88 int wGetRectPlacementInfo(WScreen * scr, WMRect rect, int *flags)
89 {
90 	int best;
91 	unsigned long area, totalArea;
92 	int i;
93 	int rx = rect.pos.x;
94 	int ry = rect.pos.y;
95 	int rw = rect.size.width;
96 	int rh = rect.size.height;
97 
98 	wassertrv(flags != NULL, 0);
99 
100 	best = -1;
101 	area = 0;
102 	totalArea = 0;
103 
104 	*flags = XFLAG_NONE;
105 
106 	if (scr->xine_info.count <= 1) {
107 		unsigned long a;
108 
109 		a = calcIntersectionArea(rx, ry, rw, rh, 0, 0, scr->scr_width, scr->scr_height);
110 
111 		if (a == 0) {
112 			*flags |= XFLAG_DEAD;
113 		} else if (a != rw * rh) {
114 			*flags |= XFLAG_PARTIAL;
115 		}
116 
117 		return scr->xine_info.primary_head;
118 	}
119 
120 	for (i = 0; i < wXineramaHeads(scr); i++) {
121 		unsigned long a;
122 
123 		a = calcIntersectionArea(rx, ry, rw, rh,
124 					 scr->xine_info.screens[i].pos.x,
125 					 scr->xine_info.screens[i].pos.y,
126 					 scr->xine_info.screens[i].size.width,
127 					 scr->xine_info.screens[i].size.height);
128 
129 		totalArea += a;
130 		if (a > area) {
131 			if (best != -1)
132 				*flags |= XFLAG_MULTIPLE;
133 			area = a;
134 			best = i;
135 		}
136 	}
137 
138 	if (best == -1) {
139 		*flags |= XFLAG_DEAD;
140 		best = wGetHeadForPointerLocation(scr);
141 	} else if (totalArea != rw * rh)
142 		*flags |= XFLAG_PARTIAL;
143 
144 	return best;
145 }
146 
147 /* get the head that covers most of the rectangle */
wGetHeadForRect(WScreen * scr,WMRect rect)148 int wGetHeadForRect(WScreen * scr, WMRect rect)
149 {
150 	int best;
151 	unsigned long area;
152 	int i;
153 	int rx = rect.pos.x;
154 	int ry = rect.pos.y;
155 	int rw = rect.size.width;
156 	int rh = rect.size.height;
157 
158 	if (!scr->xine_info.count)
159 		return scr->xine_info.primary_head;
160 
161 	best = -1;
162 	area = 0;
163 
164 	for (i = 0; i < wXineramaHeads(scr); i++) {
165 		unsigned long a;
166 
167 		a = calcIntersectionArea(rx, ry, rw, rh,
168 					 scr->xine_info.screens[i].pos.x,
169 					 scr->xine_info.screens[i].pos.y,
170 					 scr->xine_info.screens[i].size.width,
171 					 scr->xine_info.screens[i].size.height);
172 
173 		if (a > area) {
174 			area = a;
175 			best = i;
176 		}
177 	}
178 
179 	/*
180 	 * in case rect is in dead space, return valid head
181 	 */
182 	if (best == -1)
183 		best = wGetHeadForPointerLocation(scr);
184 
185 	return best;
186 }
187 
wWindowTouchesHead(WWindow * wwin,int head)188 Bool wWindowTouchesHead(WWindow * wwin, int head)
189 {
190 	WScreen *scr;
191 	WMRect rect;
192 	int a;
193 
194 	if (!wwin || !wwin->frame)
195 		return False;
196 
197 	scr = wwin->screen_ptr;
198 	rect = wGetRectForHead(scr, head);
199 	a = calcIntersectionArea(wwin->frame_x, wwin->frame_y,
200 				 wwin->frame->core->width,
201 				 wwin->frame->core->height,
202 				 rect.pos.x, rect.pos.y, rect.size.width, rect.size.height);
203 
204 	return (a != 0);
205 }
206 
wAppIconTouchesHead(WAppIcon * aicon,int head)207 Bool wAppIconTouchesHead(WAppIcon * aicon, int head)
208 {
209 	WScreen *scr;
210 	WMRect rect;
211 	int a;
212 
213 	if (!aicon || !aicon->icon)
214 		return False;
215 
216 	scr = aicon->icon->core->screen_ptr;
217 	rect = wGetRectForHead(scr, head);
218 	a = calcIntersectionArea(aicon->x_pos, aicon->y_pos,
219 				 aicon->icon->core->width,
220 				 aicon->icon->core->height,
221 				 rect.pos.x, rect.pos.y, rect.size.width, rect.size.height);
222 
223 	return (a != 0);
224 }
225 
wGetHeadForWindow(WWindow * wwin)226 int wGetHeadForWindow(WWindow * wwin)
227 {
228 	WMRect rect;
229 
230 	if (wwin == NULL || wwin->frame == NULL)
231 		return 0;
232 
233 	rect.pos.x = wwin->frame_x;
234 	rect.pos.y = wwin->frame_y;
235 	rect.size.width = wwin->frame->core->width;
236 	rect.size.height = wwin->frame->core->height;
237 
238 	return wGetHeadForRect(wwin->screen_ptr, rect);
239 }
240 
241 /* Find head on left, right, up or down direction relative to current
242 head. If there is no screen available on pointed direction, -1 will be
243 returned.*/
wGetHeadRelativeToCurrentHead(WScreen * scr,int current_head,int direction)244 int wGetHeadRelativeToCurrentHead(WScreen *scr, int current_head, int direction)
245 {
246 	short int found = 0;
247 	int i;
248 	int distance = 0;
249 	int smallest_distance = 0;
250 	int nearest_head = scr->xine_info.primary_head;
251 	WMRect crect = wGetRectForHead(scr, current_head);
252 
253 	for (i = 0; i < scr->xine_info.count; i++) {
254 		if (i == current_head)
255 			continue;
256 
257 		WMRect *rect = &scr->xine_info.screens[i];
258 
259 		/* calculate distance from the next screen to current one */
260 		switch (direction) {
261 			case DIRECTION_LEFT:
262 				if (rect->pos.x < crect.pos.x) {
263 					found = 1;
264 					distance = abs((rect->pos.x + rect->size.width)
265 							- crect.pos.x) + abs(rect->pos.y + crect.pos.y);
266 				}
267 				break;
268 			case DIRECTION_RIGHT:
269 				if (rect->pos.x > crect.pos.x) {
270 					found = 1;
271 					distance = abs((crect.pos.x + crect.size.width)
272 							- rect->pos.x) + abs(rect->pos.y + crect.pos.y);
273 				}
274 				break;
275 			case DIRECTION_UP:
276 				if (rect->pos.y < crect.pos.y) {
277 					found = 1;
278 					distance = abs((rect->pos.y + rect->size.height)
279 							- crect.pos.y) + abs(rect->pos.x + crect.pos.x);
280 				}
281 				break;
282 			case DIRECTION_DOWN:
283 				if (rect->pos.y > crect.pos.y) {
284 					found = 1;
285 					distance = abs((crect.pos.y + crect.size.height)
286 							- rect->pos.y) + abs(rect->pos.x + crect.pos.x);
287 				}
288 				break;
289 		}
290 
291 		if (found && distance == 0)
292 			return i;
293 
294 		if (smallest_distance == 0)
295 			smallest_distance = distance;
296 
297 		if (abs(distance) <= smallest_distance) {
298 			smallest_distance = distance;
299 			nearest_head = i;
300 		}
301 	}
302 
303 	if (found && smallest_distance != 0 && nearest_head != current_head)
304 		return nearest_head;
305 
306 	return -1;
307 }
308 
wGetHeadForPoint(WScreen * scr,WMPoint point)309 int wGetHeadForPoint(WScreen * scr, WMPoint point)
310 {
311 	int i;
312 
313 	for (i = 0; i < scr->xine_info.count; i++) {
314 		WMRect *rect = &scr->xine_info.screens[i];
315 
316 		if ((unsigned)(point.x - rect->pos.x) < rect->size.width &&
317 		    (unsigned)(point.y - rect->pos.y) < rect->size.height)
318 			return i;
319 	}
320 	return scr->xine_info.primary_head;
321 }
322 
wGetHeadForPointerLocation(WScreen * scr)323 int wGetHeadForPointerLocation(WScreen * scr)
324 {
325 	WMPoint point;
326 	Window bla;
327 	int ble;
328 	unsigned int blo;
329 
330 	if (!scr->xine_info.count)
331 		return scr->xine_info.primary_head;
332 
333 	if (!XQueryPointer(dpy, scr->root_win, &bla, &bla, &point.x, &point.y, &ble, &ble, &blo))
334 		return scr->xine_info.primary_head;
335 
336 	return wGetHeadForPoint(scr, point);
337 }
338 
339 /* get the dimensions of the head */
wGetRectForHead(WScreen * scr,int head)340 WMRect wGetRectForHead(WScreen * scr, int head)
341 {
342 	WMRect rect;
343 
344 	if (head < scr->xine_info.count) {
345 		rect.pos.x = scr->xine_info.screens[head].pos.x;
346 		rect.pos.y = scr->xine_info.screens[head].pos.y;
347 		rect.size.width = scr->xine_info.screens[head].size.width;
348 		rect.size.height = scr->xine_info.screens[head].size.height;
349 	} else {
350 		rect.pos.x = 0;
351 		rect.pos.y = 0;
352 		rect.size.width = scr->scr_width;
353 		rect.size.height = scr->scr_height;
354 	}
355 
356 	return rect;
357 }
358 
wGetUsableAreaForHead(WScreen * scr,int head,WArea * totalAreaPtr,Bool noicons)359 WArea wGetUsableAreaForHead(WScreen * scr, int head, WArea * totalAreaPtr, Bool noicons)
360 {
361 	WArea totalArea, usableArea;
362 	WMRect rect = wGetRectForHead(scr, head);
363 
364 	totalArea.x1 = rect.pos.x;
365 	totalArea.y1 = rect.pos.y;
366 	totalArea.x2 = totalArea.x1 + rect.size.width;
367 	totalArea.y2 = totalArea.y1 + rect.size.height;
368 
369 	if (totalAreaPtr != NULL)
370 		*totalAreaPtr = totalArea;
371 
372 	if (head < wXineramaHeads(scr)) {
373 		usableArea = noicons ? scr->totalUsableArea[head] : scr->usableArea[head];
374 	} else
375 		usableArea = totalArea;
376 
377 	if (noicons) {
378 		/* check if user wants dock covered */
379 		if (scr->dock && wPreferences.no_window_over_dock && wAppIconTouchesHead(scr->dock->icon_array[0], head)) {
380 			int offset = wPreferences.icon_size + DOCK_EXTRA_SPACE;
381 
382 			if (scr->dock->on_right_side)
383 				usableArea.x2 -= offset;
384 			else
385 				usableArea.x1 += offset;
386 		}
387 
388 		/* check if icons are on the same side as dock, and adjust if not done already */
389 		if (scr->dock && wPreferences.no_window_over_icons && !wPreferences.no_window_over_dock && (wPreferences.icon_yard & IY_VERT)) {
390 			int offset = wPreferences.icon_size + DOCK_EXTRA_SPACE;
391 
392 			if (scr->dock->on_right_side && (wPreferences.icon_yard & IY_RIGHT))
393 				usableArea.x2 -= offset;
394 			/* can't use IY_LEFT in if, it's 0 ... */
395 			if (!scr->dock->on_right_side && !(wPreferences.icon_yard & IY_RIGHT))
396 				usableArea.x1 += offset;
397 		}
398 	}
399 
400 	return usableArea;
401 }
402 
wGetPointToCenterRectInHead(WScreen * scr,int head,int width,int height)403 WMPoint wGetPointToCenterRectInHead(WScreen * scr, int head, int width, int height)
404 {
405 	WMPoint p;
406 	WMRect rect = wGetRectForHead(scr, head);
407 
408 	p.x = rect.pos.x + (rect.size.width - width) / 2;
409 	p.y = rect.pos.y + (rect.size.height - height) / 2;
410 
411 	return p;
412 }
413