Fix ExplainWindowPlacement when using "NoUSPosition" style.
[fvwm.git] / libs / FScreen.c
blob5e072b2cda3cf33df98beb8e1f461499c0adc55e
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.
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.
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 * FScreen.c
19 * Xinerama abstraction support for window manager.
21 * This module is all original code
22 * by Dmitry Yu. Bolkhovityanov <bolkhov@inp.nsk.su>
23 * Copyright 2001, Dmitry Bolkhovityanov
24 * You may use this code for any purpose, as long as the original
25 * copyright remains in the source code and all documentation
29 * Brief description of used concept:
31 * This code is always used by client, regardless of if Xinerama is
32 * available or not (either because -lXinerama was missing or
33 * because Xinerama extension is missing on display).
35 * If Xinerama is available, this module maintains a list of screens,
36 * from [1] to [num_screens]. screens[0] is always the "global" screen,
37 * so if Xinerama is unavailable or disabled, the module performs
38 * all checks on screens[0] instead of screens[1..num_screens].
40 * The client should first call the FScreenInit(), passing
41 * it the opened display descriptor. During this call the list of
42 * Xinerama screens is retrieved and 'dpy' is saved for future
43 * reference.
45 * If the client wishes to hard-disable Xinerama support (e.g. if
46 * Xinerama extension is present but is broken on display), it should
47 * call FScreenDisable() *before* FScreenInit().
49 * Using real Xinerama screens info may be switched on/off "on the
50 * fly" by calling FScreenSetState(0=off/else=on).
52 * Modules with Xinerama support should listen to the XineramaEnable
53 * and XineramaDisable strings coming over the module pipe as
54 * M_CONFIG_INFO packets and call FScreenEnable() or
55 * FScreenDisable in response.
59 #include "config.h"
61 #include <stdio.h>
62 #include <ctype.h>
64 #include "defaults.h"
65 #include "fvwmlib.h"
66 #include "Parse.h"
67 #include "FScreen.h"
68 #include "PictureBase.h"
70 #ifdef HAVE_XINERAMA
71 # undef FSCREEN_NEED_SCREENINFO
72 # define FScreenHaveXinerama 1
73 # ifdef HAVE_SOLARIS_XINERAMA
74 # define FScreenHaveSolarisXinerama 1
75 # ifdef HAVE_SOLARIS_XINERAMA_H
76 # include <X11/extensions/xinerama.h>
77 # else
78 # define FSCREEN_NEED_SOLARIS_PROTOTYPES
79 # endif
80 # define FSCREEN_NEED_SCREENINFO
81 # else
82 # define FScreenHaveSolarisXinerama 0
83 # include <X11/extensions/Xinerama.h>
84 # endif
85 #else
86 # define FSCREEN_NEED_SCREENINFO
87 # define FScreenHaveXinerama 0
88 # define FScreenHaveSolarisXinerama 0
89 #endif
91 #ifdef FSCREEN_NEED_SCREENINFO
92 typedef struct
94 int screen_number;
95 short x_org;
96 short y_org;
97 unsigned short width;
98 unsigned short height;
99 } XineramaScreenInfo;
100 #endif
102 #ifdef FSCREEN_NEED_SOLARIS_PROTOTYPES
103 /* Copied from Solaris 9's X11/extensions/xinerama.h */
104 Bool XineramaGetState(Display*, int);
105 Status XineramaGetInfo(Display*, int, XRectangle*, unsigned char*, int*);
106 Status XineramaGetCenterHint(Display*, int, int*, int*);
107 #endif
109 #if FScreenHaveSolarisXinerama == 0
110 #define XineramaGetInfo(a,b,c,d,e) 0
111 #endif
113 #if FScreenHaveXinerama
114 # if FScreenHaveSolarisXinerama
115 # define XineramaQueryExtension(d,b,c) 1 /* Lie, for now */
116 # define XineramaIsActive(d) XineramaGetState((d),0)
117 # define XineramaQueryScreens(d,s) (*(s)) = 0, NULL
118 # endif
119 #else
120 # define XineramaQueryExtension(da, b, c) 0
121 # define XineramaIsActive(a) 0
122 # define XineramaQueryScreens(d,s) (*(s)) = 0, NULL
123 #endif
125 #ifndef MAXFRAMEBUFFERS
126 #define MAXFRAMEBUFFERS 16
127 #endif
130 #ifdef USE_XINERAMA_EMULATION
131 #define FScreenXineramaEmulation 1
132 #else
133 #define FScreenXineramaEmulation 0
134 #endif
136 #if 0
137 #ifdef HAVE_RANDR
138 #include <X11/Xproto.h>
139 #include <X11/extensions/Xrandr.h>
140 #endif
141 #endif
143 enum
145 /* Replace with FSCREEN_GLOBAL to restore default behaviour */
146 DEFAULT_GEOMETRY_SCREEN = FSCREEN_PRIMARY
149 /* In fact, only corners matter -- there will never be GRAV_NONE */
150 enum {GRAV_POS = 0, GRAV_NONE = 1, GRAV_NEG = 2};
151 static int grav_matrix[3][3] =
153 { NorthWestGravity, NorthGravity, NorthEastGravity },
154 { WestGravity, CenterGravity, EastGravity },
155 { SouthWestGravity, SouthGravity, SouthEastGravity }
157 #define DEFAULT_GRAVITY NorthWestGravity
160 static Display *disp = NULL;
161 static Bool is_xinerama_enabled = DEFAULT_XINERAMA_ENABLED;
162 static Bool is_sls_enabled = False;
163 static Bool have_sls_screen_list = False;
164 static XineramaScreenInfo *screens;
165 static XineramaScreenInfo *screens_xi;
166 static XineramaScreenInfo *screens_sls = NULL;
167 /* # of Xinerama screens, *not* counting the global, 0 if disabled */
168 static int num_screens = 0;
169 /* # of Xinerama screens, *not* counting the global */
170 static int total_screens = 0;
171 static int total_screens_xi = 0;
172 static int total_screens_sls = 1;
173 static int total_sls_width = 1;
174 static int total_sls_height = 1;
175 static int first_to_check = 0;
176 static int last_to_check = 0;
177 static int default_geometry_scr = FSCREEN_PRIMARY;
178 /* only to be accessed vie the set/get functions! */
179 static int primary_scr = DEFAULT_PRIMARY_SCREEN;
181 #if 0
182 #ifdef HAVE_RANDR
183 static Bool randr_disabled = 0;
184 static Bool randr_active = 0;
185 static int randr_event_base = -1;
186 static int randr_error_base = -1;
187 #endif
188 #endif
190 static Window blank_w, vert_w, blank2_w, blank3_w;
192 static int FScreenParseScreenBit(char *arg, char default_screen);
193 static int FindScreenOfXY(int x, int y);
195 static XineramaScreenInfo *
196 solaris_XineramaQueryScreens(Display *d, int *nscreens)
198 if (FScreenHaveSolarisXinerama)
200 XineramaScreenInfo *screens = NULL;
201 XRectangle monitors[MAXFRAMEBUFFERS];
202 unsigned char hints[16];
203 int result;
205 /* dummy instructions to keep -Wall happy */
206 hints[0] = hints[0];
207 result = XineramaGetInfo(
208 d, DefaultScreen(d), monitors, hints, nscreens);
209 if (result)
211 int m;
213 /* Note, malloced area later freed by XFree() */
214 screens = (XineramaScreenInfo *)malloc(
215 sizeof(XineramaScreenInfo) * (*nscreens));
216 for (m = 0; m < *nscreens; ++m)
218 screens[m].screen_number = m;
219 screens[m].x_org = monitors[m].x;
220 screens[m].y_org = monitors[m].y;
221 screens[m].width = monitors[m].width;
222 screens[m].height = monitors[m].height;
225 else
227 fprintf(
228 stderr,
229 "Error getting Xinerama information\n");
230 *nscreens = 0;
233 return screens;
235 else
237 return NULL;
241 static XineramaScreenInfo *FXineramaQueryScreens(Display *d, int *nscreens)
243 if (FScreenHaveXinerama == 0)
245 *nscreens = 0;
247 return NULL;
249 if (FScreenHaveSolarisXinerama == 1)
251 return solaris_XineramaQueryScreens(d, nscreens);
253 else
255 return XineramaQueryScreens(d, nscreens);
259 static void GetMouseXY(XEvent *eventp, int *x, int *y)
261 if (!is_xinerama_enabled || last_to_check == first_to_check)
263 /* We use .x_org,.y_org because nothing prevents a screen to be
264 * not at (0,0) */
265 *x = screens[first_to_check].x_org;
266 *y = screens[first_to_check].y_org;
268 else
270 XEvent e;
272 if (eventp == NULL)
274 eventp = &e;
275 e.type = 0;
277 fev_get_evpos_or_query(
278 disp, DefaultRootWindow(disp), eventp, x, y);
281 return;
284 Bool FScreenIsEnabled(void)
286 return (!is_xinerama_enabled || num_screens == 0) ? False : True;
289 Bool FScreenIsSLSEnabled(void)
291 return is_sls_enabled;
294 static void FScreenUpdateEmulationMapState(void)
296 static Bool is_mapped = False;
298 if (!FScreenXineramaEmulation)
300 return;
303 if (is_xinerama_enabled && !is_sls_enabled)
305 if (!is_mapped)
307 XMapRaised(disp, blank_w);
308 XMapRaised(disp, blank2_w);
309 XMapRaised(disp, blank3_w);
310 XMapRaised(disp, vert_w);
311 is_mapped = True;
314 else
316 if (is_mapped)
318 XUnmapWindow(disp, blank_w);
319 XUnmapWindow(disp, blank2_w);
320 XUnmapWindow(disp, blank3_w);
321 XUnmapWindow(disp, vert_w);
322 is_mapped = False;
327 static void FScreenSetState(Bool do_enable)
329 is_xinerama_enabled = do_enable;
330 if (do_enable && total_screens > 0)
332 num_screens = total_screens;
333 first_to_check = 1;
334 last_to_check = total_screens;
336 else
338 num_screens = 0;
339 first_to_check = 0;
340 last_to_check = 0;
342 FScreenUpdateEmulationMapState();
346 void FScreenInit(Display *dpy)
348 static Bool is_initialised = False;
349 int dummy_rc;
351 if (is_initialised)
353 return;
355 dummy_rc = 0;
356 is_initialised = True;
357 disp = dpy;
358 if (FScreenXineramaEmulation)
360 int count;
361 int w;
362 int h;
363 int ws;
364 unsigned long scr;
365 Window root;
366 XSetWindowAttributes attributes;
368 scr = DefaultScreen(disp);
369 root = RootWindow(disp, scr);
371 /* xinerama emulation simulates xinerama on a single screen:
373 * actual screen
374 * +---------------------+--------------+
375 * | | |
376 * | | |
377 * | | |
378 * | | simulated |
379 * | | screen 2 |
380 * | simulated screen 1 | |
381 * | | |
382 * | | |
383 * | | |
384 * | | |
385 * +---------------------+ |
386 * | blank area | |
387 * | | |
388 * +---------------------+--------------+
390 count = 2;
391 total_screens_xi = count;
392 screens_xi = (XineramaScreenInfo *)
393 safemalloc(sizeof(XineramaScreenInfo) * (1 + count));
394 /* calculate the faked sub screen dimensions */
395 w = DisplayWidth(disp, scr);
396 ws = 3 * w / 5;
397 h = DisplayHeight(disp, scr);
398 screens_xi[1].screen_number = 0;
399 screens_xi[1].x_org = 0;
400 screens_xi[1].y_org = h / 16;
401 screens_xi[1].width = ws;
402 screens_xi[1].height = 7 * h / 8;
403 screens_xi[2].screen_number = 1;
404 screens_xi[2].x_org = ws;
405 screens_xi[2].y_org = 0;
406 screens_xi[2].width = w - ws;
407 screens_xi[2].height = 7 * h / 8;
408 /* add delimiter */
409 attributes.background_pixel = PictureWhitePixel();
410 attributes.override_redirect = True;
411 blank_w = XCreateWindow(
412 disp, root, 0, screens_xi[1].y_org - 1,
413 screens_xi[1].width, 2, 0, CopyFromParent,
414 CopyFromParent, CopyFromParent,
415 CWBackPixel|CWOverrideRedirect, &attributes);
416 blank2_w = XCreateWindow(
417 disp, root, 0,
418 screens_xi[1].y_org + screens_xi[1].height - 1,
419 screens_xi[1].width, 2, 0, CopyFromParent,
420 CopyFromParent, CopyFromParent,
421 CWBackPixel|CWOverrideRedirect, &attributes);
422 blank3_w = XCreateWindow(
423 disp, root, screens_xi[2].x_org,
424 screens_xi[2].height - 1, w - screens_xi[2].x_org, 2,
425 0, CopyFromParent, CopyFromParent, CopyFromParent,
426 CWBackPixel|CWOverrideRedirect, &attributes);
427 vert_w = XCreateWindow(
428 disp, root, screens_xi[2].x_org - 1, 0, 2, h, 0,
429 CopyFromParent, CopyFromParent, CopyFromParent,
430 CWBackPixel|CWOverrideRedirect, &attributes);
432 else if (FScreenHaveXinerama &&
433 XineramaQueryExtension(disp, &dummy_rc, &dummy_rc) &&
434 XineramaIsActive(disp))
436 int count;
437 XineramaScreenInfo *info;
439 info = FXineramaQueryScreens(disp, &count);
440 total_screens_xi = count;
441 screens_xi = (XineramaScreenInfo *)
442 safemalloc(sizeof(XineramaScreenInfo) *
443 (1 + count));
444 memcpy(screens_xi + 1, info,
445 sizeof(XineramaScreenInfo) * count);
446 XFree(info);
448 else
450 total_screens_xi = 0;
451 screens_xi =
452 (XineramaScreenInfo *)safemalloc(
453 sizeof(XineramaScreenInfo)*1);
455 total_screens = total_screens_xi;
456 screens = screens_xi;
458 /* Now, fill screens[0] with global screen parameters */
459 screens_xi[0].screen_number = -1;
460 screens_xi[0].x_org = 0;
461 screens_xi[0].y_org = 0;
462 screens_xi[0].width = DisplayWidth (disp, DefaultScreen(disp));
463 screens_xi[0].height = DisplayHeight(disp, DefaultScreen(disp));
465 /* Fill in the screen range */
466 FScreenSetState(is_xinerama_enabled);
468 return;
471 void FScreenOnOff(Bool do_enable)
473 FScreenSetState(do_enable);
476 void FScreenSLSOnOff(Bool do_enable)
478 is_sls_enabled = do_enable;
479 if (do_enable)
481 total_screens = total_screens_sls;
482 if (!screens_sls)
484 /* Sls not configured yet, use whole screen by default
486 FScreenConfigureSLSSize(1, 1);
488 screens = screens_sls;
490 else
492 total_screens = total_screens_xi;
493 screens = screens_xi;
495 FScreenSetState(is_xinerama_enabled);
498 Bool FScreenConfigureSLSSize(int width, int height)
500 unsigned long scr = DefaultScreen(disp);
501 int w = DisplayWidth(disp, scr);
502 int h = DisplayHeight(disp, scr);
504 if (width <= 1)
506 width = 1;
508 else if (width > w)
510 width = w;
512 if (height <= 1)
514 height = 1;
516 else if (height > h)
518 height = h;
520 if (total_sls_width == width && total_sls_height == height &&
521 screens_sls)
523 return False;
525 if (screens_sls)
527 free(screens_sls);
528 screens_sls = NULL;
530 /* calculate the screens */
532 int row, col, sn;
533 int ws;
534 int hs;
536 total_screens_sls = width * height;
537 total_sls_width = width;
538 total_sls_height = height;
539 ws = w / total_sls_width;
540 hs = h / total_sls_height;
541 screens_sls = (XineramaScreenInfo *)
542 safemalloc(sizeof(XineramaScreenInfo) *
543 (1 + total_screens_sls));
544 /* calculate the faked sub screen dimensions */
545 screens_sls[0] = screens_xi[0];
546 sn = 1;
547 for (row = 0; row < total_sls_height; row++)
549 for (col = 0; col < total_sls_width; col++, sn++)
551 screens_sls[sn].screen_number = sn - 1;
552 screens_sls[sn].x_org = col * ws;
553 screens_sls[sn].y_org = row * hs;
554 screens_sls[sn].width = ws;
555 screens_sls[sn].height = hs;
559 have_sls_screen_list = False;
560 FScreenSLSOnOff(is_sls_enabled);
562 return True;
565 Bool FScreenConfigureSLSScreens(int nscreens, char *args)
567 int sn;
568 char *next;
570 if (nscreens == 0 || args == NULL)
572 return FScreenConfigureSLSSize(1, 1);
574 if (nscreens == 1 && screens_sls)
576 return False;
578 if (screens_sls)
580 free(screens_sls);
581 screens_sls = NULL;
583 screens_sls = (XineramaScreenInfo *)
584 safemalloc(sizeof(XineramaScreenInfo) * (nscreens + 1));
585 screens_sls[0] = screens_xi[0];
586 for (sn = 1; sn <= nscreens; sn++, args = next)
588 char *token;
589 int val[4];
591 /* read next screen spec */
592 token = PeekToken(args, &next);
593 if (!token)
595 break;
597 if (XParseGeometry(token, &val[0], &val[1],
598 (unsigned int *)&val[2],
599 (unsigned int *)&val[3]) ==
600 (XValue|YValue|WidthValue|HeightValue) ||
601 GetIntegerArguments(args, &next, val, 4) == 4)
603 if (val[0] < 0 || val[1] < 0 || val[2] < 1 ||
604 val[3] < 1)
606 /* illegal spec */
607 break;
609 screens_sls[sn].screen_number = sn - 1;
610 screens_sls[sn].x_org = val[0];
611 screens_sls[sn].y_org = val[1];
612 screens_sls[sn].width = val[2];
613 screens_sls[sn].height = val[3];
615 else
617 /* illegal spec */
618 break;
621 total_screens_sls = sn - 1;
622 have_sls_screen_list = True;
623 FScreenSLSOnOff(is_sls_enabled);
625 return True;
628 #if 0
629 void FScreenDisableRandR(void)
631 if (disp != NULL)
633 fprintf(stderr, "FScreen: WARNING: FScreenDisableRandR()"
634 " called after FScreenInit()!\n");
636 randr_disabled = 1;
638 return;
640 #endif
642 static int FScreenGetPrimaryScreen(XEvent *ev)
644 if (!is_xinerama_enabled)
646 return 0;
648 if (primary_scr == FSCREEN_GLOBAL)
650 return 0;
652 else if (primary_scr == FSCREEN_CURRENT)
654 int mx;
655 int my;
657 /* use current screen as primary screen */
658 GetMouseXY(ev, &mx, &my);
659 return FindScreenOfXY(mx, my);
661 else if (primary_scr < 0 || primary_scr >= last_to_check)
663 /* out of range */
664 return 0;
667 return primary_scr + 1;
670 void FScreenSetPrimaryScreen(int scr)
672 primary_scr = scr;
675 /* Intended to be called by modules. Simply pass in the parameter from the
676 * config string sent by fvwm. */
677 void FScreenConfigureModule(char *args)
679 int val[6];
680 int n;
681 char *next;
683 n = GetIntegerArguments(args, &next, val, 4);
684 if (n != 4)
686 /* ignore broken line */
687 return;
689 FScreenSetPrimaryScreen(val[1]);
691 if (val[3])
693 /* SLS screen coordinates follow */
694 n = GetIntegerArguments(next, &next, val + 4, 1);
695 if (n != 1)
697 /* ignore broken line */
698 return;
700 FScreenConfigureSLSScreens(val[4], next);
702 else
704 /* simple SLS line */
705 n = GetIntegerArguments(next, NULL, val + 4, 2);
706 if (n != 2)
708 /* ignore broken line */
709 return;
711 FScreenConfigureSLSSize(val[4], val[5]);
714 FScreenSLSOnOff(val[2]);
715 FScreenOnOff(val[0]);
717 return;
720 /* Here's the function used by fvwm to generate the string which
721 * FScreenConfigureModule expects to receive back as its argument.
723 const char *FScreenGetConfiguration(void)
725 int i;
726 int l;
727 int l2;
728 static char msg[MAX_MODULE_INPUT_TEXT_LEN];
729 char buf[64];
731 sprintf(
732 msg, XINERAMA_CONFIG_STRING" %d %d %d %d",
733 FScreenIsEnabled(), primary_scr,
734 FScreenIsSLSEnabled(), have_sls_screen_list);
735 l = strlen(msg);
736 if (have_sls_screen_list)
738 sprintf(msg + l, " %d", total_screens_sls);
739 for (i = 0; i < total_screens_sls; i++)
741 sprintf(buf, " %d %d %d %d", screens_sls[i].x_org,
742 screens_sls[i].y_org, screens_sls[i].width,
743 screens_sls[i].height);
744 l2 = strlen(buf);
745 if (l + l2 > MAX_MODULE_INPUT_TEXT_LEN)
747 break;
749 strcat(msg + l, buf);
750 l += l2;
753 else
755 sprintf(msg + l, " %d %d", total_sls_width, total_sls_height);
758 return msg;
761 /* Sets the default screen for ...ParseGeometry if no screen spec is given.
762 * Usually this is FSCREEN_SPEC_PRIMARY, but this won't allow modules to appear
763 * under the pointer. */
764 void FScreenSetDefaultModuleScreen(char *scr_spec)
766 default_geometry_scr =
767 FScreenParseScreenBit(scr_spec, FSCREEN_SPEC_PRIMARY);
769 return;
773 static int FindScreenOfXY(int x, int y)
775 int i;
777 x = x % screens_xi[0].width;
778 while (x < 0)
780 x += screens_xi[0].width;
782 y = y % screens_xi[0].height;
783 while (y < 0)
785 y += screens_xi[0].height;
787 for (i = first_to_check; i <= last_to_check; i++)
789 if (x >= screens[i].x_org &&
790 x < screens[i].x_org + screens[i].width &&
791 y >= screens[i].y_org &&
792 y < screens[i].y_org + screens[i].height)
794 return i;
798 /* Ouch! A "black hole" coords? As for now, return global screen */
799 return 0;
802 static int FindScreen(
803 fscreen_scr_arg *arg, fscreen_scr_t screen)
805 fscreen_scr_arg tmp;
807 if (num_screens == 0)
809 screen = FSCREEN_GLOBAL;
811 switch (screen)
813 case FSCREEN_GLOBAL:
814 screen = 0;
815 break;
816 case FSCREEN_PRIMARY:
817 screen =
818 FScreenGetPrimaryScreen(
819 (arg && arg->mouse_ev) ? arg->mouse_ev : NULL);
820 break;
821 case FSCREEN_CURRENT:
822 /* translate to xypos format */
823 if (!arg)
825 tmp.mouse_ev = NULL;
826 arg = &tmp;
828 GetMouseXY(arg->mouse_ev, &arg->xypos.x, &arg->xypos.y);
829 /* fall through */
830 case FSCREEN_XYPOS:
831 /* translate to screen number */
832 if (!arg)
834 tmp.xypos.x = 0;
835 tmp.xypos.y = 0;
836 arg = &tmp;
838 screen = FindScreenOfXY(arg->xypos.x, arg->xypos.y);
839 break;
840 default:
841 /* screen is given counting from 0; translate to counting from
842 * 1 */
843 screen++;
844 break;
847 return screen;
850 /* Returns the specified screens geometry rectangle. screen can be a screen
851 * number or any of the values FSCREEN_GLOBAL, FSCREEN_CURRENT,
852 * FSCREEN_PRIMARY or FSCREEN_XYPOS. The arg union only has a meaning for
853 * FSCREEN_CURRENT and FSCREEN_XYARG. For FSCREEN_CURRENT its mouse_ev member
854 * may be given. It is tried to find out the pointer position from the event
855 * first before querying the pointer. For FSCREEN_XYPOS the xpos member is used
856 * to fetch the x/y position of the point on the screen. If arg is NULL, the
857 * position 0 0 is assumed instead.
859 * Any of the arguments arg, x, y, w and h may be NULL.
861 * FSCREEN_GLOBAL: return the global screen dimensions
862 * FSCREEN_CURRENT: return dimensions of the screen with the pointer
863 * FSCREEN_PRIMARY: return the primary screen dimensions
864 * FSCREEN_XYPOS: return dimensions of the screen with the given coordinates
866 * The function returns False if the global screen was returned and more than
867 * one screen is configured. Otherwise it returns True.
869 Bool FScreenGetScrRect(
870 fscreen_scr_arg *arg, fscreen_scr_t screen, int *x, int *y,
871 int *w, int *h)
873 screen = FindScreen(arg, screen);
874 if (screen < first_to_check || screen > last_to_check)
876 screen = 0;
878 if (x)
880 *x = screens[screen].x_org;
882 if (y)
884 *y = screens[screen].y_org;
886 if (w)
888 *w = screens[screen].width;
890 if (h)
892 *h = screens[screen].height;
895 return !(screen == 0 && num_screens > 1);
898 /* returns the screen id */
899 Bool FScreenGetScrId(
900 fscreen_scr_arg *arg, fscreen_scr_t screen)
902 screen = FindScreen(arg, screen);
903 if (screen < 0)
905 screen = FSCREEN_GLOBAL;
908 return screen;
911 /* Translates the coodinates *x *y from the screen specified by arg_src and
912 * screen_src to coordinates on the screen specified by arg_dest and
913 * screen_dest. (see FScreenGetScrRect for more details). */
914 void FScreenTranslateCoordinates(
915 fscreen_scr_arg *arg_src, fscreen_scr_t screen_src,
916 fscreen_scr_arg *arg_dest, fscreen_scr_t screen_dest,
917 int *x, int *y)
919 int x_src;
920 int y_src;
921 int x_dest;
922 int y_dest;
924 FScreenGetScrRect(arg_src, screen_src, &x_src, &y_src, NULL, NULL);
925 FScreenGetScrRect(arg_dest, screen_dest, &x_dest, &y_dest, NULL, NULL);
927 if (x)
929 *x = *x + x_src - x_dest;
931 if (y)
933 *y = *y + y_src - y_dest;
936 return;
939 /* Arguments work exactly like for FScreenGetScrRect() */
940 int FScreenClipToScreen(
941 fscreen_scr_arg *arg, fscreen_scr_t screen, int *x, int *y, int w,
942 int h)
944 int sx;
945 int sy;
946 int sw;
947 int sh;
948 int lx = (x) ? *x : 0;
949 int ly = (y) ? *y : 0;
950 int x_grav = GRAV_POS;
951 int y_grav = GRAV_POS;
953 FScreenGetScrRect(arg, screen, &sx, &sy, &sw, &sh);
954 if (lx + w > sx + sw)
956 lx = sx + sw - w;
957 x_grav = GRAV_NEG;
959 if (ly + h > sy + sh)
961 ly = sy + sh - h;
962 y_grav = GRAV_NEG;
964 if (lx < sx)
966 lx = sx;
967 x_grav = GRAV_POS;
969 if (ly < sy)
971 ly = sy;
972 y_grav = GRAV_POS;
974 if (x)
976 *x = lx;
978 if (y)
980 *y = ly;
983 return grav_matrix[y_grav][x_grav];
986 /* Arguments work exactly like for FScreenGetScrRect() */
987 void FScreenCenterOnScreen(
988 fscreen_scr_arg *arg, fscreen_scr_t screen, int *x, int *y, int w,
989 int h)
991 int sx;
992 int sy;
993 int sw;
994 int sh;
995 int lx;
996 int ly;
998 FScreenGetScrRect(arg, screen, &sx, &sy, &sw, &sh);
999 lx = (sw - w) / 2;
1000 ly = (sh - h) / 2;
1001 if (lx < 0)
1002 lx = 0;
1003 if (ly < 0)
1004 ly = 0;
1005 lx += sx;
1006 ly += sy;
1007 if (x)
1009 *x = lx;
1011 if (y)
1013 *y = ly;
1016 return;
1019 void FScreenGetResistanceRect(
1020 int wx, int wy, unsigned int ww, unsigned int wh, int *x0, int *y0,
1021 int *x1, int *y1)
1023 fscreen_scr_arg arg;
1025 arg.xypos.x = wx + ww / 2;
1026 arg.xypos.y = wy + wh / 2;
1027 FScreenGetScrRect(&arg, FSCREEN_XYPOS, x0, y0, x1, y1);
1028 *x1 += *x0;
1029 *y1 += *y0;
1031 return;
1034 /* Arguments work exactly like for FScreenGetScrRect() */
1035 Bool FScreenIsRectangleOnScreen(
1036 fscreen_scr_arg *arg, fscreen_scr_t screen, rectangle *rec)
1038 int sx;
1039 int sy;
1040 int sw;
1041 int sh;
1043 FScreenGetScrRect(arg, screen, &sx, &sy, &sw, &sh);
1045 return (rec->x + rec->width > sx && rec->x < sx + sw &&
1046 rec->y + rec->height > sy && rec->y < sy + sh) ? True : False;
1049 void FScreenSpecToString(char *dest, int space, fscreen_scr_t screen)
1051 char s[32];
1053 if (space <= 0)
1055 return;
1057 switch (screen)
1059 case FSCREEN_GLOBAL:
1060 strcpy(s, "global screen");
1061 break;
1062 case FSCREEN_CURRENT:
1063 strcpy(s, "current screen");
1064 break;
1065 case FSCREEN_PRIMARY:
1066 strcpy(s, "primary screen");
1067 break;
1068 case FSCREEN_XYPOS:
1069 strcpy(s, "screen specified by xy");
1070 break;
1071 default:
1072 sprintf(s, "%d", screen);
1073 break;
1075 strncpy(dest, s, space);
1076 dest[space - 1] = 0;
1078 return;
1081 static int FScreenParseScreenBit(char *scr_spec, char default_screen)
1083 int scr = default_geometry_scr;
1084 char c;
1086 c = (scr_spec) ? tolower(*scr_spec) : tolower(default_screen);
1087 if (c == FSCREEN_SPEC_GLOBAL)
1089 scr = FSCREEN_GLOBAL;
1091 else if (c == FSCREEN_SPEC_CURRENT)
1093 scr = FSCREEN_CURRENT;
1095 else if (c == FSCREEN_SPEC_PRIMARY)
1097 scr = FSCREEN_PRIMARY;
1099 else if (c == FSCREEN_SPEC_WINDOW)
1101 scr = FSCREEN_XYPOS;
1103 else if (isdigit(c))
1105 scr = atoi(scr_spec);
1107 else
1109 c = tolower(default_screen);
1110 if (c == FSCREEN_SPEC_GLOBAL)
1112 scr = FSCREEN_GLOBAL;
1114 else if (c == FSCREEN_SPEC_CURRENT)
1116 scr = FSCREEN_CURRENT;
1118 else if (c == FSCREEN_SPEC_PRIMARY)
1120 scr = FSCREEN_PRIMARY;
1122 else if (c == FSCREEN_SPEC_WINDOW)
1124 scr = FSCREEN_XYPOS;
1126 else if (isdigit(c))
1128 scr = atoi(scr_spec);
1132 return scr;
1135 int FScreenGetScreenArgument(char *scr_spec, fscreen_scr_spec_t default_screen)
1137 while (scr_spec && isspace(*scr_spec))
1139 scr_spec++;
1142 return FScreenParseScreenBit(scr_spec, default_screen);
1146 * FScreenParseGeometry
1147 * Does the same as XParseGeometry, but handles additional "@scr".
1148 * Since it isn't safe to define "ScreenValue" constant (actual values
1149 * of other "XXXValue" are specified in Xutil.h, not by us, so there can
1150 * be a clash), the screen value is always returned, even if it wasn't
1151 * present in `parse_string' (set to default in that case).
1154 int FScreenParseGeometryWithScreen(
1155 char *parsestring, int *x_return, int *y_return,
1156 unsigned int *width_return, unsigned int *height_return,
1157 int *screen_return)
1159 int ret;
1160 char *copy, *scr_p;
1161 int s_size;
1162 int scr;
1164 /* Safety net */
1165 if (parsestring == NULL || *parsestring == '\0')
1167 return 0;
1170 /* Make a local copy devoid of "@scr" */
1171 s_size = strlen(parsestring) + 1;
1172 copy = safemalloc(s_size);
1173 memcpy(copy, parsestring, s_size);
1174 scr_p = strchr(copy, '@');
1175 if (scr_p != NULL)
1177 *scr_p++ = '\0';
1180 /* Do the parsing */
1181 ret = XParseGeometry(
1182 copy, x_return, y_return, width_return, height_return);
1184 /* Parse the "@scr", if any */
1185 scr = FScreenParseScreenBit(scr_p, FSCREEN_SPEC_PRIMARY);
1186 *screen_return = scr;
1188 /* We don't need the string any more */
1189 free(copy);
1191 return ret;
1194 /* Same as above, but dump screen return value to keep compatible with the X
1195 * function. */
1196 int FScreenParseGeometry(
1197 char *parsestring, int *x_return, int *y_return,
1198 unsigned int *width_return, unsigned int *height_return)
1200 int scr;
1201 int rc;
1202 int mx;
1203 int my;
1205 rc = FScreenParseGeometryWithScreen(
1206 parsestring, x_return, y_return, width_return, height_return,
1207 &scr);
1208 if (rc == 0)
1210 return 0;
1212 switch (scr)
1214 case FSCREEN_GLOBAL:
1215 scr = 0;
1216 break;
1217 case FSCREEN_CURRENT:
1218 GetMouseXY(NULL, &mx, &my);
1219 scr = FindScreenOfXY(mx, my);
1220 break;
1221 case FSCREEN_PRIMARY:
1222 scr = FScreenGetPrimaryScreen(NULL);
1223 break;
1224 default:
1225 scr++;
1226 break;
1228 if (scr <= 0 || scr > last_to_check)
1230 scr = 0;
1232 else
1234 /* adapt geometry to selected screen */
1235 if (rc & XValue)
1237 if (rc & XNegative)
1239 *x_return -=
1240 (screens[0].width -
1241 screens[scr].width -
1242 screens[scr].x_org);
1244 else
1246 *x_return += screens[scr].x_org;
1249 if (rc & YValue)
1251 if (rc & YNegative)
1253 *y_return -=
1254 (screens[0].height -
1255 screens[scr].height -
1256 screens[scr].y_org);
1258 else
1260 *y_return += screens[scr].y_org;
1265 return rc;
1269 /* FScreenGetGeometry
1270 * Parses the geometry in a form: XGeometry[@screen], i.e.
1271 * [=][<width>{xX}<height>][{+-}<xoffset>{+-}<yoffset>][@<screen>]
1272 * where <screen> is either a number or "G" (global) "C" (current)
1273 * or "P" (primary)
1275 * Args:
1276 * parsestring, x_r, y_r, w_r, h_r the same as in XParseGeometry
1277 * hints window hints structure, may be NULL
1278 * flags bitmask of allowed flags (XValue, WidthValue, XNegative...)
1280 * Note1:
1281 * hints->width and hints->height will be used to calc negative geometry
1282 * if width/height isn't specified in the geometry itself.
1284 * Note2:
1285 * This function's behaviour is crafted to sutisfy/emulate the
1286 * FvwmWinList::MakeMeWindow()'s behaviour.
1288 * Note3:
1289 * A special value of `flags' when [XY]Value are there but [XY]Negative
1290 * aren't, means that in case of negative geometry specification
1291 * x_r/y_r values will be promoted to the screen border, but w/h
1292 * wouldn't be subtracted, so that the program can do x-=w later
1293 * ([XY]Negative *will* be returned, albeit absent in `flags').
1294 * This option is supposed for proggies like FvwmButtons, which
1295 * receive geometry specification long before they are able to actually
1296 * use it (and which calculate w/h themselves).
1297 * (The same effect can't be obtained with omitting {Width,Height}Value
1298 * in the flags, since the app may wish to get the dimensions but apply
1299 * some constraints later (as FvwmButtons do, BTW...).)
1300 * This option can be also useful in cases where dimensions are
1301 * specified not in pixels but in some other units (e.g., charcells).
1303 int FScreenGetGeometry(
1304 char *parsestring, int *x_return, int *y_return,
1305 int *width_return, int *height_return, XSizeHints *hints, int flags)
1307 int ret;
1308 int saved;
1309 int x, y;
1310 unsigned int w = 0, h = 0;
1311 int grav, x_grav, y_grav;
1312 int scr = default_geometry_scr;
1313 int scr_x, scr_y;
1314 int scr_w, scr_h;
1316 /* I. Do the parsing and strip off extra bits */
1317 ret = FScreenParseGeometryWithScreen(parsestring, &x, &y, &w, &h, &scr);
1318 saved = ret & (XNegative | YNegative);
1319 ret &= flags;
1321 /* II. Get the screen rectangle */
1322 switch (scr)
1324 case FSCREEN_GLOBAL:
1325 case FSCREEN_CURRENT:
1326 case FSCREEN_PRIMARY:
1327 case FSCREEN_XYPOS:
1328 FScreenGetScrRect(NULL, scr, &scr_x, &scr_y, &scr_w, &scr_h);
1329 break;
1331 default:
1332 scr++;
1333 if (scr < first_to_check || scr > last_to_check)
1334 scr = first_to_check;
1335 scr_x = screens[scr].x_org;
1336 scr_y = screens[scr].y_org;
1337 scr_w = screens[scr].width;
1338 scr_h = screens[scr].height;
1341 /* III. Interpret and fill in the values */
1343 /* Fill in dimensions for future negative calculations if
1344 * omitted/forbidden */
1345 /* Maybe should use *x_return,*y_return if hints==NULL?
1346 * Unreliable... */
1347 if (hints != NULL && hints->flags & PSize)
1349 if ((ret & WidthValue) == 0)
1351 w = hints->width;
1353 if ((ret & HeightValue) == 0)
1355 h = hints->height;
1358 else
1360 /* This branch is required for case when size *is* specified,
1361 * but masked off */
1362 if ((ret & WidthValue) == 0)
1364 w = 0;
1366 if ((ret & HeightValue) == 0)
1368 h = 0;
1372 /* Advance coords to the screen... */
1373 x += scr_x;
1374 y += scr_y;
1376 /* ...and process negative geometries */
1377 if (saved & XNegative)
1379 x += scr_w;
1381 if (saved & YNegative)
1383 y += scr_h;
1385 if (ret & XNegative)
1387 x -= w;
1389 if (ret & YNegative)
1391 y -= h;
1394 /* Restore negative bits */
1395 ret |= saved;
1397 /* Guess orientation */
1398 x_grav = (ret & XNegative)? GRAV_NEG : GRAV_POS;
1399 y_grav = (ret & YNegative)? GRAV_NEG : GRAV_POS;
1400 grav = grav_matrix[y_grav][x_grav];
1402 /* Return the values */
1403 if (ret & XValue)
1405 *x_return = x;
1406 if (hints != NULL)
1408 hints->x = x;
1411 if (ret & YValue)
1413 *y_return = y;
1414 if (hints != NULL)
1416 hints->y = y;
1419 if (ret & WidthValue)
1421 *width_return = w;
1422 if (hints != NULL)
1424 hints->width = w;
1427 if (ret & HeightValue)
1429 *height_return = h;
1430 if (hints != NULL)
1432 hints->height = h;
1435 if (1 /*flags & GravityValue*/ && grav != DEFAULT_GRAVITY)
1437 if (hints != NULL && hints->flags & PWinGravity)
1439 hints->win_gravity = grav;
1442 if (hints != NULL && ret & XValue && ret & YValue)
1443 hints->flags |= USPosition;
1445 return ret;
1448 /* FScreenMangleScreenIntoUSPosHints
1449 * A hack to mangle the screen number into the XSizeHints structure.
1450 * If the USPosition flag is set, hints->x is set to the magic number and
1451 * hints->y is set to the screen number. If the USPosition flag is clear,
1452 * x and y are set to zero.
1454 * Note: This is a *hack* to allow modules to specify the target screen for
1455 * their windows and have the StartsOnScreen style set for them at the same
1456 * time. Do *not* rely on the mechanism described above.
1458 void FScreenMangleScreenIntoUSPosHints(fscreen_scr_t screen, XSizeHints *hints)
1460 if (hints->flags & USPosition)
1462 hints->x = FSCREEN_MANGLE_USPOS_HINTS_MAGIC;
1463 hints->y = (short)screen;
1465 else
1467 hints->x = 0;
1468 hints->y = 0;
1471 return;
1474 /* FScreenMangleScreenIntoUSPosHints
1475 * A hack to mangle the screen number into the XSizeHints structure.
1476 * If the USPosition flag is set, hints->x is set to the magic number and
1477 * hints->y is set to the screen spec. If the USPosition flag is clear,
1478 * x and y are set to zero.
1480 * Note: This is a *hack* to allow modules to specify the target screen for
1481 * their windows and have the StartsOnScreen style set for them at the same
1482 * time. Do *not* rely on the mechanism described above.
1484 fscreen_scr_t FScreenFetchMangledScreenFromUSPosHints(XSizeHints *hints)
1486 fscreen_scr_t screen;
1488 if ((hints->flags & USPosition) &&
1489 hints->x == FSCREEN_MANGLE_USPOS_HINTS_MAGIC)
1491 screen = (fscreen_scr_t)(hints->y);
1493 else
1495 screen = FSCREEN_GLOBAL;
1498 return screen;
1502 /* no rand_r for now */
1503 # if 0
1504 int FScreenGetRandrEventType(void)
1506 #ifdef HAVE_RANDR
1507 return randr_active? randr_event_base + RRScreenChangeNotify : 0;
1508 #else
1509 return 0;
1510 #endif
1513 Bool FScreenHandleRandrEvent(
1514 XEvent *event, int *old_w, int *old_h, int *new_w, int *new_h)
1516 #ifndef HAVE_RANDR
1517 return 0;
1518 #else
1519 XRRScreenChangeNotifyEvent *ev = (XRRScreenChangeNotifyEvent *)event;
1520 int nw, nh, tmp;
1522 if (!randr_active ||
1523 event->type != randr_event_base + RRScreenChangeNotify)
1525 return 0;
1528 nw = ev->width;
1529 nh = ev->height;
1532 * Note1: this check is not very good, since the right way is to
1533 * obtain a list of possible rotations and ...
1535 * Note2: as to WM's point of view, I'm unsure if rotation should be
1536 * treated exactly as resizing (i.e. that it should reposition
1537 * windows in the same fashion).
1539 if (ev->rotation & (1<<1 | 1<<3))
1541 tmp = nw;
1542 nw = nh;
1543 nh = tmp;
1546 *old_w = screens[0].width;
1547 *old_h = screens[0].height;
1549 screens[0].width = nw;
1550 *new_w = nw;
1551 screens[0].height = nh;
1552 *new_h = nh;
1554 return (nw != *old_w || nh != *old_h);
1555 #endif
1557 #endif