/* -*-c-*- */ /* This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see: */ /* * FScreen.c * Xinerama abstraction support for window manager. * * This module is all original code * by Dmitry Yu. Bolkhovityanov * Copyright 2001, Dmitry Bolkhovityanov * You may use this code for any purpose, as long as the original * copyright remains in the source code and all documentation */ /* * Brief description of used concept: * * This code is always used by client, regardless of if Xinerama is * available or not (either because -lXinerama was missing or * because Xinerama extension is missing on display). * * If Xinerama is available, this module maintains a list of screens, * from [1] to [num_screens]. screens[0] is always the "global" screen, * so if Xinerama is unavailable or disabled, the module performs * all checks on screens[0] instead of screens[1..num_screens]. * * The client should first call the FScreenInit(), passing * it the opened display descriptor. During this call the list of * Xinerama screens is retrieved and 'dpy' is saved for future * reference. * * If the client wishes to hard-disable Xinerama support (e.g. if * Xinerama extension is present but is broken on display), it should * call FScreenDisable() *before* FScreenInit(). * * Using real Xinerama screens info may be switched on/off "on the * fly" by calling FScreenSetState(0=off/else=on). * * Modules with Xinerama support should listen to the XineramaEnable * and XineramaDisable strings coming over the module pipe as * M_CONFIG_INFO packets and call FScreenEnable() or * FScreenDisable in response. * */ #include "config.h" #include #include #include "defaults.h" #include "fvwmlib.h" #include "Parse.h" #include "FScreen.h" #include "PictureBase.h" #ifdef HAVE_XINERAMA # undef FSCREEN_NEED_SCREENINFO # define FScreenHaveXinerama 1 # ifdef HAVE_SOLARIS_XINERAMA # define FScreenHaveSolarisXinerama 1 # ifdef HAVE_SOLARIS_XINERAMA_H # include # else # define FSCREEN_NEED_SOLARIS_PROTOTYPES # endif # define FSCREEN_NEED_SCREENINFO # else # define FScreenHaveSolarisXinerama 0 # include # endif #else # define FSCREEN_NEED_SCREENINFO # define FScreenHaveXinerama 0 # define FScreenHaveSolarisXinerama 0 #endif #ifdef FSCREEN_NEED_SCREENINFO typedef struct { int screen_number; short x_org; short y_org; unsigned short width; unsigned short height; } XineramaScreenInfo; #endif #ifdef FSCREEN_NEED_SOLARIS_PROTOTYPES /* Copied from Solaris 9's X11/extensions/xinerama.h */ Bool XineramaGetState(Display*, int); Status XineramaGetInfo(Display*, int, XRectangle*, unsigned char*, int*); Status XineramaGetCenterHint(Display*, int, int*, int*); #endif #if FScreenHaveSolarisXinerama == 0 #define XineramaGetInfo(a,b,c,d,e) 0 #endif #if FScreenHaveXinerama # if FScreenHaveSolarisXinerama # define XineramaQueryExtension(d,b,c) 1 /* Lie, for now */ # define XineramaIsActive(d) XineramaGetState((d),0) # define XineramaQueryScreens(d,s) (*(s)) = 0, NULL # endif #else # define XineramaQueryExtension(da, b, c) 0 # define XineramaIsActive(a) 0 # define XineramaQueryScreens(d,s) (*(s)) = 0, NULL #endif #ifndef MAXFRAMEBUFFERS #define MAXFRAMEBUFFERS 16 #endif #ifdef USE_XINERAMA_EMULATION #define FScreenXineramaEmulation 1 #else #define FScreenXineramaEmulation 0 #endif #if 0 #ifdef HAVE_RANDR #include #include #endif #endif enum { /* Replace with FSCREEN_GLOBAL to restore default behaviour */ DEFAULT_GEOMETRY_SCREEN = FSCREEN_PRIMARY }; /* In fact, only corners matter -- there will never be GRAV_NONE */ enum {GRAV_POS = 0, GRAV_NONE = 1, GRAV_NEG = 2}; static int grav_matrix[3][3] = { { NorthWestGravity, NorthGravity, NorthEastGravity }, { WestGravity, CenterGravity, EastGravity }, { SouthWestGravity, SouthGravity, SouthEastGravity } }; #define DEFAULT_GRAVITY NorthWestGravity static Display *disp = NULL; static Bool is_xinerama_enabled = DEFAULT_XINERAMA_ENABLED; static Bool is_sls_enabled = False; static Bool have_sls_screen_list = False; static XineramaScreenInfo *screens; static XineramaScreenInfo *screens_xi; static XineramaScreenInfo *screens_sls = NULL; /* # of Xinerama screens, *not* counting the global, 0 if disabled */ static int num_screens = 0; /* # of Xinerama screens, *not* counting the global */ static int total_screens = 0; static int total_screens_xi = 0; static int total_screens_sls = 1; static int total_sls_width = 1; static int total_sls_height = 1; static int first_to_check = 0; static int last_to_check = 0; static int default_geometry_scr = FSCREEN_PRIMARY; /* only to be accessed vie the set/get functions! */ static int primary_scr = DEFAULT_PRIMARY_SCREEN; #if 0 #ifdef HAVE_RANDR static Bool randr_disabled = 0; static Bool randr_active = 0; static int randr_event_base = -1; static int randr_error_base = -1; #endif #endif static Window blank_w, vert_w, blank2_w, blank3_w; static int FScreenParseScreenBit(char *arg, char default_screen); static int FindScreenOfXY(int x, int y); static XineramaScreenInfo * solaris_XineramaQueryScreens(Display *d, int *nscreens) { if (FScreenHaveSolarisXinerama) { XineramaScreenInfo *screens = NULL; XRectangle monitors[MAXFRAMEBUFFERS]; unsigned char hints[16]; int result; /* dummy instructions to keep -Wall happy */ hints[0] = hints[0]; result = XineramaGetInfo( d, DefaultScreen(d), monitors, hints, nscreens); if (result) { int m; /* Note, malloced area later freed by XFree() */ screens = (XineramaScreenInfo *)malloc( sizeof(XineramaScreenInfo) * (*nscreens)); for (m = 0; m < *nscreens; ++m) { screens[m].screen_number = m; screens[m].x_org = monitors[m].x; screens[m].y_org = monitors[m].y; screens[m].width = monitors[m].width; screens[m].height = monitors[m].height; } } else { fprintf( stderr, "Error getting Xinerama information\n"); *nscreens = 0; } return screens; } else { return NULL; } } static XineramaScreenInfo *FXineramaQueryScreens(Display *d, int *nscreens) { if (FScreenHaveXinerama == 0) { *nscreens = 0; return NULL; } if (FScreenHaveSolarisXinerama == 1) { return solaris_XineramaQueryScreens(d, nscreens); } else { return XineramaQueryScreens(d, nscreens); } } static void GetMouseXY(XEvent *eventp, int *x, int *y) { if (!is_xinerama_enabled || last_to_check == first_to_check) { /* We use .x_org,.y_org because nothing prevents a screen to be * not at (0,0) */ *x = screens[first_to_check].x_org; *y = screens[first_to_check].y_org; } else { XEvent e; if (eventp == NULL) { eventp = &e; e.type = 0; } fev_get_evpos_or_query( disp, DefaultRootWindow(disp), eventp, x, y); } return; } Bool FScreenIsEnabled(void) { return (!is_xinerama_enabled || num_screens == 0) ? False : True; } Bool FScreenIsSLSEnabled(void) { return is_sls_enabled; } static void FScreenUpdateEmulationMapState(void) { static Bool is_mapped = False; if (!FScreenXineramaEmulation) { return; } if (is_xinerama_enabled && !is_sls_enabled) { if (!is_mapped) { XMapRaised(disp, blank_w); XMapRaised(disp, blank2_w); XMapRaised(disp, blank3_w); XMapRaised(disp, vert_w); is_mapped = True; } } else { if (is_mapped) { XUnmapWindow(disp, blank_w); XUnmapWindow(disp, blank2_w); XUnmapWindow(disp, blank3_w); XUnmapWindow(disp, vert_w); is_mapped = False; } } } static void FScreenSetState(Bool do_enable) { is_xinerama_enabled = do_enable; if (do_enable && total_screens > 0) { num_screens = total_screens; first_to_check = 1; last_to_check = total_screens; } else { num_screens = 0; first_to_check = 0; last_to_check = 0; } FScreenUpdateEmulationMapState(); } void FScreenInit(Display *dpy) { static Bool is_initialised = False; int dummy_rc; SUPPRESS_UNUSED_VAR_WARNING(dummy_rc); if (is_initialised) { return; } is_initialised = True; disp = dpy; if (FScreenXineramaEmulation) { int count; int w; int h; int ws; unsigned long scr; Window root; XSetWindowAttributes attributes; scr = DefaultScreen(disp); root = RootWindow(disp, scr); /* xinerama emulation simulates xinerama on a single screen: * * actual screen * +---------------------+--------------+ * | | | * | | | * | | | * | | simulated | * | | screen 2 | * | simulated screen 1 | | * | | | * | | | * | | | * | | | * +---------------------+ | * | blank area | | * | | | * +---------------------+--------------+ */ count = 2; total_screens_xi = count; screens_xi = (XineramaScreenInfo *) safemalloc(sizeof(XineramaScreenInfo) * (1 + count)); /* calculate the faked sub screen dimensions */ w = DisplayWidth(disp, scr); ws = 3 * w / 5; h = DisplayHeight(disp, scr); screens_xi[1].screen_number = 0; screens_xi[1].x_org = 0; screens_xi[1].y_org = h / 16; screens_xi[1].width = ws; screens_xi[1].height = 7 * h / 8; screens_xi[2].screen_number = 1; screens_xi[2].x_org = ws; screens_xi[2].y_org = 0; screens_xi[2].width = w - ws; screens_xi[2].height = 7 * h / 8; /* add delimiter */ attributes.background_pixel = PictureWhitePixel(); attributes.override_redirect = True; blank_w = XCreateWindow( disp, root, 0, screens_xi[1].y_org - 1, screens_xi[1].width, 2, 0, CopyFromParent, CopyFromParent, CopyFromParent, CWBackPixel|CWOverrideRedirect, &attributes); blank2_w = XCreateWindow( disp, root, 0, screens_xi[1].y_org + screens_xi[1].height - 1, screens_xi[1].width, 2, 0, CopyFromParent, CopyFromParent, CopyFromParent, CWBackPixel|CWOverrideRedirect, &attributes); blank3_w = XCreateWindow( disp, root, screens_xi[2].x_org, screens_xi[2].height - 1, w - screens_xi[2].x_org, 2, 0, CopyFromParent, CopyFromParent, CopyFromParent, CWBackPixel|CWOverrideRedirect, &attributes); vert_w = XCreateWindow( disp, root, screens_xi[2].x_org - 1, 0, 2, h, 0, CopyFromParent, CopyFromParent, CopyFromParent, CWBackPixel|CWOverrideRedirect, &attributes); } else if (FScreenHaveXinerama && XineramaQueryExtension(disp, &dummy_rc, &dummy_rc) && XineramaIsActive(disp)) { int count; XineramaScreenInfo *info; info = FXineramaQueryScreens(disp, &count); total_screens_xi = count; screens_xi = (XineramaScreenInfo *) safemalloc(sizeof(XineramaScreenInfo) * (1 + count)); memcpy(screens_xi + 1, info, sizeof(XineramaScreenInfo) * count); XFree(info); } else { total_screens_xi = 0; screens_xi = (XineramaScreenInfo *)safemalloc( sizeof(XineramaScreenInfo)*1); } total_screens = total_screens_xi; screens = screens_xi; /* Now, fill screens[0] with global screen parameters */ screens_xi[0].screen_number = -1; screens_xi[0].x_org = 0; screens_xi[0].y_org = 0; screens_xi[0].width = DisplayWidth (disp, DefaultScreen(disp)); screens_xi[0].height = DisplayHeight(disp, DefaultScreen(disp)); /* Fill in the screen range */ FScreenSetState(is_xinerama_enabled); return; } void FScreenOnOff(Bool do_enable) { FScreenSetState(do_enable); } void FScreenSLSOnOff(Bool do_enable) { is_sls_enabled = do_enable; if (do_enable) { total_screens = total_screens_sls; if (!screens_sls) { /* Sls not configured yet, use whole screen by default */ FScreenConfigureSLSSize(1, 1); } screens = screens_sls; } else { total_screens = total_screens_xi; screens = screens_xi; } FScreenSetState(is_xinerama_enabled); } Bool FScreenConfigureSLSSize(int width, int height) { unsigned long scr = DefaultScreen(disp); int w = DisplayWidth(disp, scr); int h = DisplayHeight(disp, scr); if (width <= 1) { width = 1; } else if (width > w) { width = w; } if (height <= 1) { height = 1; } else if (height > h) { height = h; } if (total_sls_width == width && total_sls_height == height && screens_sls) { return False; } if (screens_sls) { free(screens_sls); screens_sls = NULL; } /* calculate the screens */ { int row, col, sn; int ws; int hs; total_screens_sls = width * height; total_sls_width = width; total_sls_height = height; ws = w / total_sls_width; hs = h / total_sls_height; screens_sls = (XineramaScreenInfo *) safemalloc(sizeof(XineramaScreenInfo) * (1 + total_screens_sls)); /* calculate the faked sub screen dimensions */ screens_sls[0] = screens_xi[0]; sn = 1; for (row = 0; row < total_sls_height; row++) { for (col = 0; col < total_sls_width; col++, sn++) { screens_sls[sn].screen_number = sn - 1; screens_sls[sn].x_org = col * ws; screens_sls[sn].y_org = row * hs; screens_sls[sn].width = ws; screens_sls[sn].height = hs; } } } have_sls_screen_list = False; FScreenSLSOnOff(is_sls_enabled); return True; } Bool FScreenConfigureSLSScreens(int nscreens, char *args) { int sn; char *next; if (nscreens == 0 || args == NULL) { return FScreenConfigureSLSSize(1, 1); } if (nscreens == 1 && screens_sls) { return False; } if (screens_sls) { free(screens_sls); screens_sls = NULL; } screens_sls = (XineramaScreenInfo *) safemalloc(sizeof(XineramaScreenInfo) * (nscreens + 1)); screens_sls[0] = screens_xi[0]; for (sn = 1; sn <= nscreens; sn++, args = next) { char *token; int val[4]; /* read next screen spec */ token = PeekToken(args, &next); if (!token) { break; } if (XParseGeometry(token, &val[0], &val[1], (unsigned int *)&val[2], (unsigned int *)&val[3]) == (XValue|YValue|WidthValue|HeightValue) || GetIntegerArguments(args, &next, val, 4) == 4) { if (val[0] < 0 || val[1] < 0 || val[2] < 1 || val[3] < 1) { /* illegal spec */ break; } screens_sls[sn].screen_number = sn - 1; screens_sls[sn].x_org = val[0]; screens_sls[sn].y_org = val[1]; screens_sls[sn].width = val[2]; screens_sls[sn].height = val[3]; } else { /* illegal spec */ break; } } total_screens_sls = sn - 1; have_sls_screen_list = True; FScreenSLSOnOff(is_sls_enabled); return True; } #if 0 void FScreenDisableRandR(void) { if (disp != NULL) { fprintf(stderr, "FScreen: WARNING: FScreenDisableRandR()" " called after FScreenInit()!\n"); } randr_disabled = 1; return; } #endif static int FScreenGetPrimaryScreen(XEvent *ev) { if (!is_xinerama_enabled) { return 0; } if (primary_scr == FSCREEN_GLOBAL) { return 0; } else if (primary_scr == FSCREEN_CURRENT) { int mx; int my; /* use current screen as primary screen */ GetMouseXY(ev, &mx, &my); return FindScreenOfXY(mx, my); } else if (primary_scr < 0 || primary_scr >= last_to_check) { /* out of range */ return 0; } return primary_scr + 1; } void FScreenSetPrimaryScreen(int scr) { primary_scr = scr; } /* Intended to be called by modules. Simply pass in the parameter from the * config string sent by fvwm. */ void FScreenConfigureModule(char *args) { int val[6]; int n; char *next; n = GetIntegerArguments(args, &next, val, 4); if (n != 4) { /* ignore broken line */ return; } FScreenSetPrimaryScreen(val[1]); if (val[3]) { /* SLS screen coordinates follow */ n = GetIntegerArguments(next, &next, val + 4, 1); if (n != 1) { /* ignore broken line */ return; } FScreenConfigureSLSScreens(val[4], next); } else { /* simple SLS line */ n = GetIntegerArguments(next, NULL, val + 4, 2); if (n != 2) { /* ignore broken line */ return; } FScreenConfigureSLSSize(val[4], val[5]); } FScreenSLSOnOff(val[2]); FScreenOnOff(val[0]); return; } /* Here's the function used by fvwm to generate the string which * FScreenConfigureModule expects to receive back as its argument. */ const char *FScreenGetConfiguration(void) { int i; int l; int l2; static char msg[MAX_MODULE_INPUT_TEXT_LEN]; char buf[64]; sprintf( msg, XINERAMA_CONFIG_STRING" %d %d %d %d", FScreenIsEnabled(), primary_scr, FScreenIsSLSEnabled(), have_sls_screen_list); l = strlen(msg); if (have_sls_screen_list) { sprintf(msg + l, " %d", total_screens_sls); for (i = 0; i < total_screens_sls; i++) { sprintf(buf, " %d %d %d %d", screens_sls[i].x_org, screens_sls[i].y_org, screens_sls[i].width, screens_sls[i].height); l2 = strlen(buf); if (l + l2 > MAX_MODULE_INPUT_TEXT_LEN) { break; } strcat(msg + l, buf); l += l2; } } else { sprintf(msg + l, " %d %d", total_sls_width, total_sls_height); } return msg; } /* Sets the default screen for ...ParseGeometry if no screen spec is given. * Usually this is FSCREEN_SPEC_PRIMARY, but this won't allow modules to appear * under the pointer. */ void FScreenSetDefaultModuleScreen(char *scr_spec) { default_geometry_scr = FScreenParseScreenBit(scr_spec, FSCREEN_SPEC_PRIMARY); return; } static int FindScreenOfXY(int x, int y) { int i; x = x % screens_xi[0].width; while (x < 0) { x += screens_xi[0].width; } y = y % screens_xi[0].height; while (y < 0) { y += screens_xi[0].height; } for (i = first_to_check; i <= last_to_check; i++) { if (x >= screens[i].x_org && x < screens[i].x_org + screens[i].width && y >= screens[i].y_org && y < screens[i].y_org + screens[i].height) { return i; } } /* Ouch! A "black hole" coords? As for now, return global screen */ return 0; } static int FindScreen( fscreen_scr_arg *arg, fscreen_scr_t screen) { fscreen_scr_arg tmp; if (num_screens == 0) { screen = FSCREEN_GLOBAL; } switch (screen) { case FSCREEN_GLOBAL: screen = 0; break; case FSCREEN_PRIMARY: screen = FScreenGetPrimaryScreen( (arg && arg->mouse_ev) ? arg->mouse_ev : NULL); break; case FSCREEN_CURRENT: /* translate to xypos format */ if (!arg) { tmp.mouse_ev = NULL; arg = &tmp; } GetMouseXY(arg->mouse_ev, &arg->xypos.x, &arg->xypos.y); /* fall through */ case FSCREEN_XYPOS: /* translate to screen number */ if (!arg) { tmp.xypos.x = 0; tmp.xypos.y = 0; arg = &tmp; } screen = FindScreenOfXY(arg->xypos.x, arg->xypos.y); break; default: /* screen is given counting from 0; translate to counting from * 1 */ screen++; break; } return screen; } /* Given pointer coordinates, return the screen the pointer is on. * * Perhaps most useful with $[pointer.screen] */ int FScreenOfPointerXY(int x, int y) { int pscreen; pscreen = FindScreenOfXY(x, y); return (pscreen > 0) ? --pscreen : pscreen; } /* Returns the specified screens geometry rectangle. screen can be a screen * number or any of the values FSCREEN_GLOBAL, FSCREEN_CURRENT, * FSCREEN_PRIMARY or FSCREEN_XYPOS. The arg union only has a meaning for * FSCREEN_CURRENT and FSCREEN_XYARG. For FSCREEN_CURRENT its mouse_ev member * may be given. It is tried to find out the pointer position from the event * first before querying the pointer. For FSCREEN_XYPOS the xpos member is used * to fetch the x/y position of the point on the screen. If arg is NULL, the * position 0 0 is assumed instead. * * Any of the arguments arg, x, y, w and h may be NULL. * * FSCREEN_GLOBAL: return the global screen dimensions * FSCREEN_CURRENT: return dimensions of the screen with the pointer * FSCREEN_PRIMARY: return the primary screen dimensions * FSCREEN_XYPOS: return dimensions of the screen with the given coordinates * * The function returns False if the global screen was returned and more than * one screen is configured. Otherwise it returns True. */ Bool FScreenGetScrRect( fscreen_scr_arg *arg, fscreen_scr_t screen, int *x, int *y, int *w, int *h) { screen = FindScreen(arg, screen); if (screen < first_to_check || screen > last_to_check) { screen = 0; } if (x) { *x = screens[screen].x_org; } if (y) { *y = screens[screen].y_org; } if (w) { *w = screens[screen].width; } if (h) { *h = screens[screen].height; } return !(screen == 0 && num_screens > 1); } /* returns the screen id */ Bool FScreenGetScrId( fscreen_scr_arg *arg, fscreen_scr_t screen) { screen = FindScreen(arg, screen); if (screen < 0) { screen = FSCREEN_GLOBAL; } return screen; } /* Translates the coodinates *x *y from the screen specified by arg_src and * screen_src to coordinates on the screen specified by arg_dest and * screen_dest. (see FScreenGetScrRect for more details). */ void 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) { int x_src; int y_src; int x_dest; int y_dest; FScreenGetScrRect(arg_src, screen_src, &x_src, &y_src, NULL, NULL); FScreenGetScrRect(arg_dest, screen_dest, &x_dest, &y_dest, NULL, NULL); if (x) { *x = *x + x_src - x_dest; } if (y) { *y = *y + y_src - y_dest; } return; } /* Arguments work exactly like for FScreenGetScrRect() */ int FScreenClipToScreen( fscreen_scr_arg *arg, fscreen_scr_t screen, int *x, int *y, int w, int h) { int sx; int sy; int sw; int sh; int lx = (x) ? *x : 0; int ly = (y) ? *y : 0; int x_grav = GRAV_POS; int y_grav = GRAV_POS; FScreenGetScrRect(arg, screen, &sx, &sy, &sw, &sh); if (lx + w > sx + sw) { lx = sx + sw - w; x_grav = GRAV_NEG; } if (ly + h > sy + sh) { ly = sy + sh - h; y_grav = GRAV_NEG; } if (lx < sx) { lx = sx; x_grav = GRAV_POS; } if (ly < sy) { ly = sy; y_grav = GRAV_POS; } if (x) { *x = lx; } if (y) { *y = ly; } return grav_matrix[y_grav][x_grav]; } /* Arguments work exactly like for FScreenGetScrRect() */ void FScreenCenterOnScreen( fscreen_scr_arg *arg, fscreen_scr_t screen, int *x, int *y, int w, int h) { int sx; int sy; int sw; int sh; int lx; int ly; FScreenGetScrRect(arg, screen, &sx, &sy, &sw, &sh); lx = (sw - w) / 2; ly = (sh - h) / 2; if (lx < 0) lx = 0; if (ly < 0) ly = 0; lx += sx; ly += sy; if (x) { *x = lx; } if (y) { *y = ly; } return; } void FScreenGetResistanceRect( int wx, int wy, unsigned int ww, unsigned int wh, int *x0, int *y0, int *x1, int *y1) { fscreen_scr_arg arg; arg.xypos.x = wx + ww / 2; arg.xypos.y = wy + wh / 2; FScreenGetScrRect(&arg, FSCREEN_XYPOS, x0, y0, x1, y1); *x1 += *x0; *y1 += *y0; return; } /* Arguments work exactly like for FScreenGetScrRect() */ Bool FScreenIsRectangleOnScreen( fscreen_scr_arg *arg, fscreen_scr_t screen, rectangle *rec) { int sx; int sy; int sw; int sh; FScreenGetScrRect(arg, screen, &sx, &sy, &sw, &sh); return (rec->x + rec->width > sx && rec->x < sx + sw && rec->y + rec->height > sy && rec->y < sy + sh) ? True : False; } void FScreenSpecToString(char *dest, int space, fscreen_scr_t screen) { char s[32]; if (space <= 0) { return; } switch (screen) { case FSCREEN_GLOBAL: strcpy(s, "global screen"); break; case FSCREEN_CURRENT: strcpy(s, "current screen"); break; case FSCREEN_PRIMARY: strcpy(s, "primary screen"); break; case FSCREEN_XYPOS: strcpy(s, "screen specified by xy"); break; default: sprintf(s, "%d", screen); break; } strncpy(dest, s, space); dest[space - 1] = 0; return; } static int FScreenParseScreenBit(char *scr_spec, char default_screen) { int scr = default_geometry_scr; char c; c = (scr_spec) ? tolower(*scr_spec) : tolower(default_screen); if (c == FSCREEN_SPEC_GLOBAL) { scr = FSCREEN_GLOBAL; } else if (c == FSCREEN_SPEC_CURRENT) { scr = FSCREEN_CURRENT; } else if (c == FSCREEN_SPEC_PRIMARY) { scr = FSCREEN_PRIMARY; } else if (c == FSCREEN_SPEC_WINDOW) { scr = FSCREEN_XYPOS; } else if (isdigit(c)) { scr = atoi(scr_spec); } else { c = tolower(default_screen); if (c == FSCREEN_SPEC_GLOBAL) { scr = FSCREEN_GLOBAL; } else if (c == FSCREEN_SPEC_CURRENT) { scr = FSCREEN_CURRENT; } else if (c == FSCREEN_SPEC_PRIMARY) { scr = FSCREEN_PRIMARY; } else if (c == FSCREEN_SPEC_WINDOW) { scr = FSCREEN_XYPOS; } else if (isdigit(c)) { scr = atoi(scr_spec); } } return scr; } int FScreenGetScreenArgument(char *scr_spec, fscreen_scr_spec_t default_screen) { while (scr_spec && isspace(*scr_spec)) { scr_spec++; } return FScreenParseScreenBit(scr_spec, default_screen); } /* * FScreenParseGeometry * Does the same as XParseGeometry, but handles additional "@scr". * Since it isn't safe to define "ScreenValue" constant (actual values * of other "XXXValue" are specified in Xutil.h, not by us, so there can * be a clash), the screen value is always returned, even if it wasn't * present in `parse_string' (set to default in that case). * */ int FScreenParseGeometryWithScreen( char *parsestring, int *x_return, int *y_return, unsigned int *width_return, unsigned int *height_return, int *screen_return) { int ret; char *copy, *scr_p; int s_size; int scr; /* Safety net */ if (parsestring == NULL || *parsestring == '\0') { return 0; } /* Make a local copy devoid of "@scr" */ s_size = strlen(parsestring) + 1; copy = safemalloc(s_size); memcpy(copy, parsestring, s_size); scr_p = strchr(copy, '@'); if (scr_p != NULL) { *scr_p++ = '\0'; } /* Do the parsing */ ret = XParseGeometry( copy, x_return, y_return, width_return, height_return); /* Parse the "@scr", if any */ scr = FScreenParseScreenBit(scr_p, FSCREEN_SPEC_PRIMARY); *screen_return = scr; /* We don't need the string any more */ free(copy); return ret; } /* Same as above, but dump screen return value to keep compatible with the X * function. */ int FScreenParseGeometry( char *parsestring, int *x_return, int *y_return, unsigned int *width_return, unsigned int *height_return) { int scr; int rc; int mx; int my; rc = FScreenParseGeometryWithScreen( parsestring, x_return, y_return, width_return, height_return, &scr); if (rc == 0) { return 0; } switch (scr) { case FSCREEN_GLOBAL: scr = 0; break; case FSCREEN_CURRENT: GetMouseXY(NULL, &mx, &my); scr = FindScreenOfXY(mx, my); break; case FSCREEN_PRIMARY: scr = FScreenGetPrimaryScreen(NULL); break; default: scr++; break; } if (scr <= 0 || scr > last_to_check) { scr = 0; } else { /* adapt geometry to selected screen */ if (rc & XValue) { if (rc & XNegative) { *x_return -= (screens[0].width - screens[scr].width - screens[scr].x_org); } else { *x_return += screens[scr].x_org; } } if (rc & YValue) { if (rc & YNegative) { *y_return -= (screens[0].height - screens[scr].height - screens[scr].y_org); } else { *y_return += screens[scr].y_org; } } } return rc; } /* FScreenGetGeometry * Parses the geometry in a form: XGeometry[@screen], i.e. * [=][{xX}][{+-}{+-}][@] * where is either a number or "G" (global) "C" (current) * or "P" (primary) * * Args: * parsestring, x_r, y_r, w_r, h_r the same as in XParseGeometry * hints window hints structure, may be NULL * flags bitmask of allowed flags (XValue, WidthValue, XNegative...) * * Note1: * hints->width and hints->height will be used to calc negative geometry * if width/height isn't specified in the geometry itself. * * Note2: * This function's behaviour is crafted to sutisfy/emulate the * FvwmWinList::MakeMeWindow()'s behaviour. * * Note3: * A special value of `flags' when [XY]Value are there but [XY]Negative * aren't, means that in case of negative geometry specification * x_r/y_r values will be promoted to the screen border, but w/h * wouldn't be subtracted, so that the program can do x-=w later * ([XY]Negative *will* be returned, albeit absent in `flags'). * This option is supposed for proggies like FvwmButtons, which * receive geometry specification long before they are able to actually * use it (and which calculate w/h themselves). * (The same effect can't be obtained with omitting {Width,Height}Value * in the flags, since the app may wish to get the dimensions but apply * some constraints later (as FvwmButtons do, BTW...).) * This option can be also useful in cases where dimensions are * specified not in pixels but in some other units (e.g., charcells). */ int FScreenGetGeometry( char *parsestring, int *x_return, int *y_return, int *width_return, int *height_return, XSizeHints *hints, int flags) { int ret; int saved; int x, y; unsigned int w = 0, h = 0; int grav, x_grav, y_grav; int scr = default_geometry_scr; int scr_x, scr_y; int scr_w, scr_h; /* I. Do the parsing and strip off extra bits */ ret = FScreenParseGeometryWithScreen(parsestring, &x, &y, &w, &h, &scr); saved = ret & (XNegative | YNegative); ret &= flags; /* II. Get the screen rectangle */ switch (scr) { case FSCREEN_GLOBAL: case FSCREEN_CURRENT: case FSCREEN_PRIMARY: case FSCREEN_XYPOS: FScreenGetScrRect(NULL, scr, &scr_x, &scr_y, &scr_w, &scr_h); break; default: scr++; if (scr < first_to_check || scr > last_to_check) scr = first_to_check; scr_x = screens[scr].x_org; scr_y = screens[scr].y_org; scr_w = screens[scr].width; scr_h = screens[scr].height; } /* III. Interpret and fill in the values */ /* Fill in dimensions for future negative calculations if * omitted/forbidden */ /* Maybe should use *x_return,*y_return if hints==NULL? * Unreliable... */ if (hints != NULL && hints->flags & PSize) { if ((ret & WidthValue) == 0) { w = hints->width; } if ((ret & HeightValue) == 0) { h = hints->height; } } else { /* This branch is required for case when size *is* specified, * but masked off */ if ((ret & WidthValue) == 0) { w = 0; } if ((ret & HeightValue) == 0) { h = 0; } } /* Advance coords to the screen... */ x += scr_x; y += scr_y; /* ...and process negative geometries */ if (saved & XNegative) { x += scr_w; } if (saved & YNegative) { y += scr_h; } if (ret & XNegative) { x -= w; } if (ret & YNegative) { y -= h; } /* Restore negative bits */ ret |= saved; /* Guess orientation */ x_grav = (ret & XNegative)? GRAV_NEG : GRAV_POS; y_grav = (ret & YNegative)? GRAV_NEG : GRAV_POS; grav = grav_matrix[y_grav][x_grav]; /* Return the values */ if (ret & XValue) { *x_return = x; if (hints != NULL) { hints->x = x; } } if (ret & YValue) { *y_return = y; if (hints != NULL) { hints->y = y; } } if (ret & WidthValue) { *width_return = w; if (hints != NULL) { hints->width = w; } } if (ret & HeightValue) { *height_return = h; if (hints != NULL) { hints->height = h; } } if (1 /*flags & GravityValue*/ && grav != DEFAULT_GRAVITY) { if (hints != NULL && hints->flags & PWinGravity) { hints->win_gravity = grav; } } if (hints != NULL && ret & XValue && ret & YValue) hints->flags |= USPosition; return ret; } /* FScreenMangleScreenIntoUSPosHints * A hack to mangle the screen number into the XSizeHints structure. * If the USPosition flag is set, hints->x is set to the magic number and * hints->y is set to the screen number. If the USPosition flag is clear, * x and y are set to zero. * * Note: This is a *hack* to allow modules to specify the target screen for * their windows and have the StartsOnScreen style set for them at the same * time. Do *not* rely on the mechanism described above. */ void FScreenMangleScreenIntoUSPosHints(fscreen_scr_t screen, XSizeHints *hints) { if (hints->flags & USPosition) { hints->x = FSCREEN_MANGLE_USPOS_HINTS_MAGIC; hints->y = (short)screen; } else { hints->x = 0; hints->y = 0; } return; } /* FScreenMangleScreenIntoUSPosHints * A hack to mangle the screen number into the XSizeHints structure. * If the USPosition flag is set, hints->x is set to the magic number and * hints->y is set to the screen spec. If the USPosition flag is clear, * x and y are set to zero. * * Note: This is a *hack* to allow modules to specify the target screen for * their windows and have the StartsOnScreen style set for them at the same * time. Do *not* rely on the mechanism described above. */ fscreen_scr_t FScreenFetchMangledScreenFromUSPosHints(XSizeHints *hints) { fscreen_scr_t screen; if ((hints->flags & USPosition) && hints->x == FSCREEN_MANGLE_USPOS_HINTS_MAGIC) { screen = (fscreen_scr_t)(hints->y); } else { screen = FSCREEN_GLOBAL; } return screen; } /* no rand_r for now */ # if 0 int FScreenGetRandrEventType(void) { #ifdef HAVE_RANDR return randr_active? randr_event_base + RRScreenChangeNotify : 0; #else return 0; #endif } Bool FScreenHandleRandrEvent( XEvent *event, int *old_w, int *old_h, int *new_w, int *new_h) { #ifndef HAVE_RANDR return 0; #else XRRScreenChangeNotifyEvent *ev = (XRRScreenChangeNotifyEvent *)event; int nw, nh, tmp; if (!randr_active || event->type != randr_event_base + RRScreenChangeNotify) { return 0; } nw = ev->width; nh = ev->height; /* * Note1: this check is not very good, since the right way is to * obtain a list of possible rotations and ... * * Note2: as to WM's point of view, I'm unsure if rotation should be * treated exactly as resizing (i.e. that it should reposition * windows in the same fashion). */ if (ev->rotation & (1<<1 | 1<<3)) { tmp = nw; nw = nh; nh = tmp; } *old_w = screens[0].width; *old_h = screens[0].height; screens[0].width = nw; *new_w = nw; screens[0].height = nh; *new_h = nh; return (nw != *old_w || nh != *old_h); #endif } #endif