/* * Copyright (C) 2000-2007 Carsten Haitzler, Geoff Harrison and various contributors * Copyright (C) 2004-2015 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 "config.h" #if USE_XRANDR #include #endif #include "E.h" #include "borders.h" #include "ewins.h" #include "focus.h" #include "hints.h" #include "xwin.h" #define DEBUG_ZOOM 0 #if DEBUG_ZOOM #define Dprintf(fmt...) if(EDebug(EDBUG_TYPE_ZOOM))Eprintf(fmt) #else #define Dprintf(fmt...) #endif static EWin *zoom_last_ewin = NULL; static signed char zoom_can = 0; #if USE_ZOOM_XF86VM #include static int std_vid_modes_num = 0; static int std_vid_mode_cur = 0; static XF86VidModeModeInfo **std_vid_modes = NULL; static XF86VidModeModeInfo * FindMode(int w, int h) { XF86VidModeModeInfo *chosen = NULL; int i, closest = 0x7fffffff; #if USE_XRANDR if ((Mode.screen.rotation == RR_Rotate_90) || (Mode.screen.rotation == RR_Rotate_270)) { i = w; w = h; h = i; } #endif for (i = 0; i < std_vid_modes_num; i++) { int value = 0x7fffffff; if ((std_vid_modes[i]->hdisplay >= w) && (std_vid_modes[i]->vdisplay >= h)) value = ((std_vid_modes[i]->hdisplay - w) + (std_vid_modes[i]->vdisplay - h)); if (value < closest) { closest = value; chosen = std_vid_modes[i]; } } return chosen; } static int GetModeIndex(unsigned int dotclock, XF86VidModeModeLine * line) { int i; const XF86VidModeModeInfo *info; for (i = 0; i < std_vid_modes_num; i++) { info = std_vid_modes[i]; if (info->dotclock == dotclock && info->hdisplay == line->hdisplay && info->vdisplay == line->vdisplay) return i; } return 0; } static int SwitchRes(char inout, int x, int y, int w, int h, int *dw, int *dh) { static int vp_x, vp_y; XF86VidModeModeInfo *mode; int scr; scr = Dpy.screen; if (inout) { XF86VidModeModeLine curmode; int dotclock; int rx, ry; if (!XF86VidModeGetModeLine(disp, scr, &dotclock, &curmode)) return 0; XF86VidModeGetViewPort(disp, scr, &vp_x, &vp_y); mode = FindMode(w, h); if (mode) { #if USE_XRANDR int vw, vh; vw = WinGetW(VROOT); vh = WinGetH(VROOT); /* x and y relative to unrotated display */ if (Mode.screen.rotation == RR_Rotate_90) { rx = y; ry = vw - mode->vdisplay - x; } else if (Mode.screen.rotation == RR_Rotate_270) { rx = vh - mode->hdisplay - y; ry = x; } else if (Mode.screen.rotation == RR_Rotate_180) { rx = vw - mode->hdisplay - x; ry = vh - mode->vdisplay - y; } else #endif { rx = x; ry = y; } #if USE_XRANDR if ((Mode.screen.rotation == RR_Rotate_90) || (Mode.screen.rotation == RR_Rotate_270)) { *dw = mode->vdisplay; *dh = mode->hdisplay; } else #endif { *dw = mode->hdisplay; *dh = mode->vdisplay; } XF86VidModeLockModeSwitch(disp, scr, 0); std_vid_mode_cur = GetModeIndex(dotclock, &curmode); XF86VidModeSwitchToMode(disp, scr, mode); XF86VidModeSetViewPort(disp, scr, rx, ry); XF86VidModeLockModeSwitch(disp, scr, 1); return 1; } } else { mode = std_vid_modes[std_vid_mode_cur]; XF86VidModeLockModeSwitch(disp, scr, 0); XF86VidModeSwitchToMode(disp, scr, mode); XF86VidModeSetViewPort(disp, scr, vp_x, vp_y); #if 0 /* No, don't lock or we can't switch resolution */ XF86VidModeLockModeSwitch(disp, scr, 1); #endif } return 0; } static void ZoomInit(void) { int ev_base, er_base; zoom_can = -1; if (!XF86VidModeQueryExtension(disp, &ev_base, &er_base)) return; XF86VidModeGetAllModeLines(disp, Dpy.screen, &std_vid_modes_num, &std_vid_modes); if (std_vid_modes_num > 1) zoom_can = 1; } #else /* USE_ZOOM_XF86VM */ static int SetMode(XRRScreenResources * xsr, RRCrtc crtc, RRMode mode, Rotation rot) { return !XRRSetCrtcConfig(disp, xsr, crtc, EGetTimestamp(), 0, 0, mode, rot, xsr->outputs, 1); } static void SetPanning(XRRScreenResources * xsr, RRCrtc crtc, int on) { XRRPanning *xpa; xpa = XRRGetPanning(disp, xsr, crtc); if (!xpa) return; Dprintf("Panning-A: %d,%d %dx%d trk: %d,%d %dx%d bdr: %d,%d,%d,%d\n", xpa->left, xpa->top, xpa->width, xpa->height, xpa->track_left, xpa->track_top, xpa->track_width, xpa->track_height, xpa->border_left, xpa->border_top, xpa->border_right, xpa->border_bottom); xpa->timestamp = EGetTimestamp(); xpa->left = xpa->top = 0; if (on) { xpa->width = WinGetW(VROOT); xpa->height = WinGetH(VROOT); xpa->track_width = xpa->track_height = 1; } else { xpa->width = xpa->height = 0; xpa->track_width = xpa->track_height = 0; } xpa->track_left = xpa->track_top = 0; xpa->border_left = xpa->border_top = 0; xpa->border_right = xpa->border_bottom = 0; Dprintf("Panning-B: %d,%d %dx%d trk: %d,%d %dx%d bdr: %d,%d,%d,%d\n", xpa->left, xpa->top, xpa->width, xpa->height, xpa->track_left, xpa->track_top, xpa->track_width, xpa->track_height, xpa->border_left, xpa->border_top, xpa->border_right, xpa->border_bottom); XRRSetPanning(disp, xsr, crtc, xpa); XRRFreePanning(xpa); } #define SWAP(a, b) \ do { int tmp; tmp = a; a = b; b = tmp; } while(0) static RRMode ss_mode; static Rotation ss_rot; static RRMode FindMode(XRRScreenResources * xsr, int w, int h, int *dw, int *dh) { int i, in, norm, best; RRMode mode; #if DEBUG_ZOOM int ic = 0; #endif if (Mode.screen.rotation & (RR_Rotate_90 | RR_Rotate_270)) SWAP(w, h); mode = 0; best = 0x7fffffff; in = -1; for (i = 0; i < xsr->nmode; i++) { Dprintf("Sz%2d: %dx%d\n", i, xsr->modes[i].width, xsr->modes[i].height); #if DEBUG_ZOOM if (ss_mode == xsr->modes[i].id) ic = i; #endif if ((int)xsr->modes[i].width < w || (int)xsr->modes[i].height < h) continue; norm = xsr->modes[i].width - w + xsr->modes[i].height - h; if (norm >= best) continue; in = i; /* new best */ best = norm; mode = xsr->modes[i].id; } Dprintf("Cur i=%d mode=%#x WxH=%dx%d rot=%d\n", ic, (unsigned int)xsr->modes[ic].id, xsr->modes[ic].width, xsr->modes[ic].height, ss_rot); if (in < 0) return 0; /* Found a new mode */ Dprintf("New i=%d mode=%#x WxH=%dx%d rot=%d\n", in, (unsigned int)xsr->modes[in].id, xsr->modes[in].width, xsr->modes[in].height, ss_rot); w = xsr->modes[in].width; h = xsr->modes[in].height; if (Mode.screen.rotation & (RR_Rotate_90 | RR_Rotate_270)) SWAP(w, h); *dw = w; *dh = h; return mode; } static int SwitchRes(char inout, int x __UNUSED__, int y __UNUSED__, int w, int h, int *dw, int *dh) { XRRScreenResources *xsr; XRRCrtcInfo *xci; RRCrtc crtc; RRMode ss_mode_new; int ok = 0; Dprintf("%s: inout=%d\n", __func__, inout); xsr = XRRGetScreenResourcesCurrent(disp, WinGetXwin(VROOT)); if (!xsr) goto done; crtc = xsr->crtcs[0]; /* FIXME - Which crtc? */ if (inout) { /* Save current setup */ xci = XRRGetCrtcInfo(disp, xsr, crtc); if (!xci) goto done; ss_mode = xci->mode; ss_rot = xci->rotation; XRRFreeCrtcInfo(xci); /* Select zoomed setup */ ss_mode_new = FindMode(xsr, w, h, dw, dh); /* Set zoomed setup */ SetPanning(xsr, crtc, 1); ok = SetMode(xsr, crtc, ss_mode_new, ss_rot); } else { /* Revert to original setup */ ok = SetMode(xsr, crtc, ss_mode, ss_rot); SetPanning(xsr, crtc, 0); } done: if (xsr) XRRFreeScreenResources(xsr); Dprintf("%s: ok=%d\n", __func__, ok); return ok; } static void ZoomInit(void) { zoom_can = 1; } #endif /* USE_ZOOM_XF86VM */ static int zw, zh; static void _ZoomEwinRestore(EWin * ewin) { EwinBorderSetTo(ewin, ewin->normal_border); ewin->state.zoomed = 0; EwinMoveResize(ewin, ewin->save_fs.x, ewin->save_fs.y, ewin->client.w, ewin->client.h, 0); } /* outstanding BUG: zooming on shaped windows leaves stuff exposed beneath them..... */ void Zoom(EWin * ewin, int on) { if (Mode.wm.window) return; if (!ewin) ewin = zoom_last_ewin; if (!ewin) return; if (zoom_can == 0) ZoomInit(); if (zoom_can <= 0) return; Dprintf("%s: on=%d\n", __func__, on); if (!on) { /* Unzoom */ if (ewin != zoom_last_ewin) return; _ZoomEwinRestore(ewin); SwitchRes(0, 0, 0, 0, 0, NULL, NULL); zw = zh = 0; zoom_last_ewin = NULL; } else if (ewin == zoom_last_ewin) { /* Already zoomed */ return; } else { /* Zoom */ if (ewin->state.fullscreen) return; if (!zoom_last_ewin) /* first zoom */ { on = SwitchRes(1, 0, 0, ewin->client.w, ewin->client.h, &zw, &zh); } else /* we are zoomed in on another window already.... */ { _ZoomEwinRestore(zoom_last_ewin); if ((ewin->client.w <= zw) && (ewin->client.h <= zh) && ((ewin->client.w >= zw / 2) || (ewin->client.h >= zh / 2))) { /* YAY no need to change resolution :-D */ } else { /* SwitchRes only tracks the LAST mode, so we have to switch back to * the original mode before switching to new target mode * so that we can restore the original vid mode when we zoom * out of the last window ;( */ SwitchRes(0, 0, 0, 0, 0, NULL, NULL); on = SwitchRes(1, 0, 0, ewin->client.w, ewin->client.h, &zw, &zh); } } Dprintf("%s: SwitchRes=%d - client size %dx%d -> screen %dx%d\n", __func__, on, ewin->client.w, ewin->client.h, zw, zh); if (!on) return; ewin->save_fs.x = EoGetX(ewin); ewin->save_fs.y = EoGetY(ewin); EwinRaise(ewin); EwinBorderSetTo(ewin, BorderCreateFiller(ewin->client.w, ewin->client.h, zw, zh)); EwinMoveResize(ewin, 0, 0, ewin->client.w, ewin->client.h, 0); ewin->state.zoomed = 1; FocusToEWin(ewin, FOCUS_SET); zoom_last_ewin = ewin; } EwinWarpTo(ewin, 1); ESync(0); EwinStateUpdate(ewin); HintsSetWindowState(ewin); } void ReZoom(EWin * ewin __UNUSED__) { #if 0 /* Move/resize is disabled while zoomed */ if (zoom_last_ewin && ewin == zoom_last_ewin) { Zoom(ewin, 0); Zoom(ewin, 1); } #endif }