/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * 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 3 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 . * */ #include "common/system.h" #include "common/rect.h" #include "graphics/surface.h" #include "m4/gui/gui_vmng_screen.h" #include "m4/gui/gui_vmng.h" #include "m4/vars.h" namespace M4 { static void vmng_black_out_video(int32 x1, int32 y1, int32 x2, int32 y2); static bool MoveScreen(ScreenContext *myScreen, int32 parmX, int32 parmY, bool deltaMove); bool GetScreenCoords(void *scrnContent, int32 *x1, int32 *y1, int32 *x2, int32 *y2) { ScreenContext *myScreen; if ((myScreen = vmng_screen_find(scrnContent, nullptr)) == nullptr) return false; if (x1) *x1 = myScreen->x1; if (y1) *y1 = myScreen->y1; if (x2) *x2 = myScreen->x2; if (y2) *y2 = myScreen->y2; return true; } void RestoreScreens(int32 updateX1, int32 updateY1, int32 updateX2, int32 updateY2) { ScreenContext *myScreen, *tempScreen; RectList *updateRectList, *scrnUpdateList, *tempRect; if (!_G(vmng_Initted) || _G(system_shutting_down)) { return; } // Clip to screen if (updateX1 < MIN_VIDEO_X) updateX1 = MIN_VIDEO_X; if (updateY1 < MIN_VIDEO_Y) updateY1 = MIN_VIDEO_Y; if (updateX2 > MAX_VIDEO_X) updateX2 = MAX_VIDEO_X; if (updateY2 > MAX_VIDEO_Y) updateY2 = MAX_VIDEO_Y; // Throw away inside out rectangles if ((updateX2 < updateX1) || (updateY2 < updateY1)) { return; } // Create a master updateRectList - when this list is empty, the screen has been redrawn updateRectList = vmng_CreateNewRect(updateX1, updateY1, updateX2, updateY2); // First loop through the screens back to front, drawing only the transparent screens myScreen = _G(backScreen); while (myScreen && updateRectList) { // We only draw transparent screens on the first pass if ((myScreen->scrnFlags & SF_TRANSPARENT) && myScreen->redraw) { // Duplicate the updateRectList scrnUpdateList = vmng_DuplicateRectList(updateRectList); // Now clip the scrnUpdateList with the coordinates of myScreen, see if there is anything visible vmng_ClipRectList(&scrnUpdateList, myScreen->x1, myScreen->y1, myScreen->x2, myScreen->y2); // Now remove the rects of all screens blocking the scrnUpdateList tempScreen = myScreen->infront; while (scrnUpdateList && tempScreen) { vmng_RemoveRectFromRectList(&scrnUpdateList, tempScreen->x1, tempScreen->y1, tempScreen->x2, tempScreen->y2); tempScreen = tempScreen->infront; } // If there is anything left of the scrnUpdateList, redraw it if (scrnUpdateList) { (myScreen->redraw)((void *)myScreen, (void *)scrnUpdateList, nullptr, 0, 0); // Now remove all the updated rects from the updateRectList tempRect = scrnUpdateList; while (tempRect && updateRectList) { vmng_RemoveRectFromRectList(&updateRectList, tempRect->x1, tempRect->y1, tempRect->x2, tempRect->y2); tempRect = tempRect->next; } // Now turf the scrnUpdateList vmng_DisposeRectList(&scrnUpdateList); } } // Get the next screen myScreen = myScreen->infront; } // Now, draw from front to back the non-transparent screens. This way, we can detect when the entire update region has been // filled, however, if one transparent screen is infront of another, the back transparent screen must be drawn first. myScreen = _G(frontScreen); while (myScreen && updateRectList) { // Make sure it isn't a transparent screen if (!(myScreen->scrnFlags & SF_TRANSPARENT) && myScreen->redraw) { // Duplicate the updateRectList scrnUpdateList = vmng_DuplicateRectList(updateRectList); // Now clip the scrnUpdateList with the coordinates of myScreen, see what has to be redrawn vmng_ClipRectList(&scrnUpdateList, myScreen->x1, myScreen->y1, myScreen->x2, myScreen->y2); // If there is anything visible, redraw it if (scrnUpdateList) { myScreen->redraw((void *)myScreen, (void *)scrnUpdateList, nullptr, 0, 0); // Now remove all the updated rects from the updateRectList tempRect = scrnUpdateList; while (tempRect && updateRectList) { vmng_RemoveRectFromRectList(&updateRectList, tempRect->x1, tempRect->y1, tempRect->x2, tempRect->y2); tempRect = tempRect->next; } // Now turf the scrnUpdateList vmng_DisposeRectList(&scrnUpdateList); } } // Get the next screen myScreen = myScreen->behind; } // Now, if there is anything left, it should be filled in with black if (updateRectList) { tempRect = updateRectList; while (tempRect) { vmng_black_out_video(tempRect->x1, tempRect->y1, tempRect->x2, tempRect->y2); tempRect = tempRect->next; } // Now turf the updateRectList vmng_DisposeRectList(&updateRectList); } } void RestoreScreensInContext(int32 x1, int32 y1, int32 x2, int32 y2, ScreenContext *myScreen) { ScreenContext *tempScreen; //verify the gui has been initted if (!_G(vmng_Initted)) { return; } //verify parameters tempScreen = _G(frontScreen); while (tempScreen && (tempScreen != myScreen)) { tempScreen = tempScreen->behind; } if (!tempScreen) { return; } //now restore screens x1 += myScreen->x1; y1 += myScreen->y1; x2 += myScreen->x1; y2 += myScreen->y1; //restore video RestoreScreens(x1, y1, x2, y2); } bool ResizeScreen(void *scrnContent, int32 newW, int32 newH) { ScreenContext *myScreen; int32 status, oldX2, oldY2; if ((newW <= 0) || (newH <= 0)) return false; if ((myScreen = vmng_screen_find(scrnContent, &status)) == nullptr) return false; oldX2 = myScreen->x2; myScreen->x2 = myScreen->x1 + newW - 1; if (myScreen->x2 < oldX2) { RestoreScreens(myScreen->x2 + 1, myScreen->y1, oldX2, myScreen->y2); } oldY2 = myScreen->y2; myScreen->y2 = myScreen->y1 + newH - 1; if (myScreen->y2 < oldY2) { RestoreScreens(myScreen->x1, myScreen->y2 + 1, myScreen->x2, oldY2); } return true; } static void vmng_black_out_video(int32 x1, int32 y1, int32 x2, int32 y2) { Common::Rect r(x1, y1, x2 + 1, y2 + 1); g_system->fillScreen(r, 0); } bool AddScreenHotkey(void *scrnContent, int32 myKey, HotkeyCB callback) { ScreenContext *myScreen; Hotkey *myHotkey; if ((myScreen = vmng_screen_find(scrnContent, nullptr)) == nullptr) return false; if ((myHotkey = (Hotkey *)mem_alloc(sizeof(Hotkey), "hotkey")) == nullptr) return false; myHotkey->myKey = myKey; myHotkey->callback = callback; myHotkey->next = myScreen->scrnHotkeys; myScreen->scrnHotkeys = myHotkey; return true; } bool RemoveScreenHotkey(void *scrnContent, int32 myKey) { ScreenContext *myScreen; Hotkey *myHotkey, *tempHotkey; if ((myScreen = vmng_screen_find(scrnContent, nullptr)) == nullptr) return false; myHotkey = myScreen->scrnHotkeys; if (myHotkey->myKey == myKey) { myScreen->scrnHotkeys = myHotkey->next; mem_free(myHotkey); } else { while (myHotkey->next && (myHotkey->next->myKey != myKey)) { myHotkey = myHotkey->next; } if (myHotkey->next) { tempHotkey = myHotkey->next; myHotkey->next = tempHotkey->next; mem_free(tempHotkey); } else { return false; } } return true; } bool MoveScreenAbs(ScreenContext *myScreen, int32 parmX, int32 parmY) { return MoveScreen(myScreen, parmX, parmY, false); } bool MoveScreenDelta(ScreenContext *myScreen, int32 parmX, int32 parmY) { return MoveScreen(myScreen, parmX, parmY, true); } bool MoveScreenDelta(int32 parmX, int32 parmY) { return MoveScreen(_G(game_buff_ptr), parmX, parmY, true); } void vmng_screen_to_back(void *scrnContent) { ScreenContext *myScreen, *tempScreen; if ((myScreen = ExtractScreen(scrnContent, SCRN_ANY)) == nullptr) return; if (!_G(backScreen)) { myScreen->infront = nullptr; myScreen->behind = nullptr; _G(frontScreen) = myScreen; _G(backScreen) = myScreen; } else { tempScreen = _G(backScreen); while (tempScreen && ((tempScreen->scrnFlags & SF_LAYER) < (myScreen->scrnFlags & SF_LAYER))) { tempScreen = tempScreen->infront; } if (!tempScreen) { myScreen->infront = nullptr; myScreen->behind = _G(frontScreen); _G(frontScreen)->infront = myScreen; _G(frontScreen) = myScreen; } else if (tempScreen == _G(backScreen)) { myScreen->infront = _G(backScreen); myScreen->behind = nullptr; _G(backScreen)->behind = myScreen; _G(backScreen) = myScreen; } else { myScreen->infront = tempScreen; myScreen->behind = tempScreen->behind; tempScreen->behind = myScreen; myScreen->behind->infront = myScreen; } } RestoreScreens(myScreen->x1, myScreen->y1, myScreen->x2, myScreen->y2); } static bool MoveScreen(ScreenContext *myScreen, int32 parmX, int32 parmY, bool deltaMove) { int32 origX1, origY1, origX2, origY2; if (!_G(vmng_Initted)) return false; origX1 = myScreen->x1; origY1 = myScreen->y1; origX2 = myScreen->x2; origY2 = myScreen->y2; if (!deltaMove) { parmX -= origX1; parmY -= origY1; } if (!(myScreen->scrnFlags & SF_OFFSCRN)) { if ((myScreen->x2 + parmX) > MAX_VIDEO_X) parmX = MAX_VIDEO_X - myScreen->x2; else if ((myScreen->x1 + parmX) < 0) parmX = -myScreen->x1; if ((myScreen->y2 + parmY) > MAX_VIDEO_Y) parmY = MAX_VIDEO_Y - myScreen->y2; else if ((myScreen->y1 + parmY) < 0) parmY = -myScreen->y1; } if (!(parmX || parmY)) return false; myScreen->x1 += parmX; myScreen->y1 += parmY; myScreen->x2 += parmX; myScreen->y2 += parmY; if (parmY > 0) { RestoreScreens(origX1, origY1, origX2, myScreen->y1 - 1); if (parmX > 0) { RestoreScreens(origX1, myScreen->y1, myScreen->x1 - 1, origY2); } else if (parmX < 0) { RestoreScreens(myScreen->x2 + 1, myScreen->y1, origX2, origY2); } } else if (parmY < 0) { RestoreScreens(origX1, myScreen->y2 + 1, origX2, origY2); if (parmX > 0) { RestoreScreens(origX1, origY1, myScreen->x1 - 1, myScreen->y2); } else if (parmX < 0) { RestoreScreens(myScreen->x2 + 1, origY1, origX2, myScreen->y2); } } else if (parmX > 0) { RestoreScreens(origX1, origY1, myScreen->x1 - 1, origY2); } else { RestoreScreens(myScreen->x2 + 1, origY1, origX2, origY2); } RestoreScreens(myScreen->x1, myScreen->y1, myScreen->x2, myScreen->y2); return true; } } // End of namespace M4