/* * Copyright (C) 2000-2007 Carsten Haitzler, Geoff Harrison and various contributors * Copyright (C) 2003-2023 Kim Woelders * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies of the Software, its documentation and marketing & publicity * materials, and acknowledgment shall be given in the documentation, materials * and software packages that this Software was used. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "E.h" #include "ewins.h" #include "ipc.h" #include "screen.h" #include "xwin.h" typedef struct { int type; int head; int x, y; int w, h; } EScreen; static EScreen *p_screens = NULL; static int n_screens = 0; static void _ScreenAdd(int type, int head, int x, int y, unsigned int w, unsigned int h) { EScreen *es; n_screens++; p_screens = EREALLOC(EScreen, p_screens, n_screens); es = p_screens + n_screens - 1; es->type = type; es->head = head; es->x = x; es->y = y; es->w = w; es->h = h; } int ScreenGetCurrent(void) { int px, py; if (n_screens <= 1) return -1; /* Only one head */ EQueryPointer(NULL, &px, &py, NULL, NULL); return ScreenGetHead(px, py); } #if USE_XRANDR #include #define RANDR_VERSION VERS(RANDR_MAJOR, RANDR_MINOR) static void _ScreenInitXrandr(void) { #if RANDR_VERSION >= VERS(1, 2) /* >= 1.2 */ XRRScreenResources *psr; XRRCrtcInfo *pci; int i; psr = XRRGetScreenResources(disp, WinGetXwin(VROOT)); if (!psr) return; for (i = 0; i < psr->ncrtc; i++) { pci = XRRGetCrtcInfo(disp, psr, psr->crtcs[i]); if (!pci) break; if (pci->width == 0 || pci->height == 0 || pci->noutput == 0) goto next; _ScreenAdd(0, i, pci->x, pci->y, pci->width, pci->height); next: XRRFreeCrtcInfo(pci); } XRRFreeScreenResources(psr); #endif } static void _ScreenShowInfoXrandr(void) { #if RANDR_VERSION >= VERS(1, 2) /* >= 1.2 */ char buf[4096]; XRRScreenResources *psr; XRRCrtcInfo *pci; XRROutputInfo *poi; XRRModeInfo *pmi; int i, j, l; psr = XRRGetScreenResources(disp, WinGetXwin(VROOT)); if (!psr) return; IpcPrintf("Crtc ID X,Y WxH mode rot nout\n"); for (i = 0; i < psr->ncrtc; i++) { pci = XRRGetCrtcInfo(disp, psr, psr->crtcs[i]); if (!pci) break; IpcPrintf("%3d %#05lx %4d,%4d %4ux%4u %#05lx %4d %5d\n", i, psr->crtcs[i], pci->x, pci->y, pci->width, pci->height, pci->mode, pci->rotation, pci->noutput); XRRFreeCrtcInfo(pci); } IpcPrintf ("Outp ID Name WxH crtc Crtcs clOnes Modes\n"); for (i = 0; i < psr->noutput; i++) { poi = XRRGetOutputInfo(disp, psr, psr->outputs[i]); if (!poi) break; l = Esnprintf(buf, sizeof(buf), "%3d %#05lx %-10s %4lux%4lu %#05lx", i, psr->outputs[i], poi->name, poi->mm_width, poi->mm_height, poi->crtc); l += Esnprintf(buf + l, sizeof(buf) - l, " c:"); for (j = 0; j < poi->ncrtc; j++) l += Esnprintf(buf + l, sizeof(buf) - l, " %#05lx", poi->crtcs[j]); l += Esnprintf(buf + l, sizeof(buf) - l, " o:"); for (j = 0; j < poi->nclone; j++) l += Esnprintf(buf + l, sizeof(buf) - l, " %#05lx", poi->clones[j]); l += Esnprintf(buf + l, sizeof(buf) - l, " m:"); for (j = 0; j < poi->nmode; j++) l += Esnprintf(buf + l, sizeof(buf) - l, " %#05lx", poi->modes[j]); IpcPrintf("%s\n", buf); XRRFreeOutputInfo(poi); } IpcPrintf("Mode ID Name WxH\n"); for (i = 0; i < psr->nmode; i++) { pmi = psr->modes + i; IpcPrintf("%3d %#05lx %-10s %4ux%4u\n", i, pmi->id, pmi->name, pmi->width, pmi->height); } XRRFreeScreenResources(psr); #endif } #endif /* USE_XRANDR */ #if USE_XINERAMA #include static XineramaScreenInfo * _EXineramaQueryScreens(int *number) { int event_base, error_base; *number = 0; if (!XineramaQueryExtension(disp, &event_base, &error_base)) return NULL; return XineramaQueryScreens(disp, number); } static void _ScreenInitXinerama(void) { XineramaScreenInfo *screens; int i, num_screens; screens = _EXineramaQueryScreens(&num_screens); if (num_screens > 1) { for (i = 0; i < num_screens; i++) _ScreenAdd(0, screens[i].screen_number, screens[i].x_org, screens[i].y_org, screens[i].width, screens[i].height); } if (screens) XFree(screens); } static void _ScreenShowInfoXinerama(void) { static const char *const mt[] = { "Off", "On", "TV", "???" }; XineramaScreenInfo *scrns; int i, num, mode; scrns = _EXineramaQueryScreens(&num); mode = (XineramaIsActive(disp)) ? 1 : 0; if (!mode && num > 1) mode = 2; IpcPrintf("Xinerama mode: %s\n", mt[mode]); if (scrns) { IpcPrintf("Xinerama screens:\n"); for (i = 0; i < num; i++) IpcPrintf(" %2d %2d %5d %5d %5d %5d\n", i, scrns[i].screen_number, scrns[i].x_org, scrns[i].y_org, scrns[i].width, scrns[i].height); XFree(scrns); } } #endif /* USE_XINERAMA */ void ScreenInit(void) { n_screens = 0; /* Causes reconfiguration */ if (Mode.wm.window) return; #if USE_XRANDR _ScreenInitXrandr(); #endif #if USE_XINERAMA if (n_screens == 0) /* Only if not added in randr code */ _ScreenInitXinerama(); #endif } void ScreenSplit(unsigned int nx, unsigned int ny) { unsigned int i, j; if (nx > 8 || ny > 8) /* At least some limit */ return; if (nx == 0 || ny == 0) { ScreenInit(); return; } n_screens = 0; /* Causes reconfiguration */ for (i = 0; i < nx; i++) for (j = 0; j < ny; j++) _ScreenAdd(1, Dpy.screen, i * WinGetW(VROOT) / nx, j * WinGetH(VROOT) / ny, WinGetW(VROOT) / nx, WinGetH(VROOT) / ny); } void ScreenShowInfo(const char *prm __UNUSED__) { int i; IpcPrintf("Head Screen X-Origin Y-Origin Width Height\n"); IpcPrintf("Screen:\n"); IpcPrintf(" %2d %2d %5d %5d %5d %5d\n", 0, Dpy.screen, 0, 0, WinGetW(VROOT), WinGetH(VROOT)); #if USE_XRANDR _ScreenShowInfoXrandr(); #endif #if USE_XINERAMA _ScreenShowInfoXinerama(); #endif if (n_screens) { IpcPrintf("E-screens:\n"); for (i = 0; i < n_screens; i++) { EScreen *ps = p_screens + i; IpcPrintf(" %2d %2d %5d %5d %5d %5d\n", i, ps->head, ps->x, ps->y, ps->w, ps->h); } } } void ScreenGetGeometryByHead(int head, Area *pa) { EScreen *ps; if (head >= 0 && head < n_screens) { ps = p_screens + head; pa->x = ps->x; pa->y = ps->y; pa->w = ps->w; pa->h = ps->h; } else { pa->x = 0; pa->y = 0; pa->w = WinGetW(VROOT); pa->h = WinGetH(VROOT); } } int ScreenGetHead(int xi, int yi) { int i, dx, dy, dist, head; EScreen *ps; head = 0; dist = 2147483647; if (n_screens > 1) { for (i = 0; i < n_screens; i++) { ps = p_screens + i; if (xi >= ps->x && xi < ps->x + ps->w && yi >= ps->y && yi < ps->y + ps->h) { /* Inside - done */ head = i; break; } dx = xi - (ps->x + ps->w / 2); dy = yi - (ps->y + ps->h / 2); dx = dx * dx + dy * dy; if (dx >= dist) continue; dist = dx; head = i; } } return head; } int ScreenGetGeometry(int xi, int yi, Area *pa) { int head; head = ScreenGetHead(xi, yi); ScreenGetGeometryByHead(head, pa); return head; } static void _VRootGetAvailableArea(Area *pa) { EWin *const *lst, *ewin; int i, num, l, r, t, b; l = Conf.place.screen_struts.left; r = Conf.place.screen_struts.right; t = Conf.place.screen_struts.top; b = Conf.place.screen_struts.bottom; lst = EwinListGetAll(&num); for (i = 0; i < num; i++) { ewin = lst[i]; if (l < ewin->strut.left) l = ewin->strut.left; if (r < ewin->strut.right) r = ewin->strut.right; if (t < ewin->strut.top) t = ewin->strut.top; if (b < ewin->strut.bottom) b = ewin->strut.bottom; } pa->x = l; pa->y = t; pa->w = WinGetW(VROOT) - (l + r); pa->h = WinGetH(VROOT) - (t + b); } int ScreenGetAvailableArea(int xi, int yi, Area *pa, int ignore_struts) { int head; Area area; /* Available */ head = ScreenGetGeometry(xi, yi, pa); if (!ignore_struts) { _VRootGetAvailableArea(&area); if (pa->x < area.x) pa->x = area.x; if (pa->y < area.y) pa->y = area.y; if (pa->w > area.w) pa->w = area.w; if (pa->h > area.h) pa->h = area.h; } return head; } int ScreenGetGeometryByPointer(Area *pa) { int pointer_x, pointer_y; EQueryPointer(NULL, &pointer_x, &pointer_y, NULL, NULL); return ScreenGetGeometry(pointer_x, pointer_y, pa); } int ScreenGetAvailableAreaByPointer(Area *pa, int ignore_struts) { int pointer_x, pointer_y; EQueryPointer(NULL, &pointer_x, &pointer_y, NULL, NULL); return ScreenGetAvailableArea(pointer_x, pointer_y, pa, ignore_struts); }