/* 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 "m4/gui/gui_mouse.h" #include "m4/gui/gui_vmng.h" #include "m4/core/imath.h" #include "m4/core/mouse.h" #include "m4/graphics/gr_sprite.h" #include "m4/mem/res.h" #include "m4/vars.h" namespace M4 { static void transShow(void *s, void *r, void *b, int32 destX, int32 destY); bool gui_mouse_init() { _G(mouseBuffer).data = nullptr; if ((_G(mouseBuffer).data = (uint8 *)mem_alloc(32 * 32, "mouse graphic")) == nullptr) { return false; } _G(mouseBuffer).w = 32; _G(mouseBuffer).stride = 32; _G(mouseBuffer).h = 32; auto &mouseSprite = _G(mouseSprite); if (!mouseSprite) { if ((mouseSprite = (M4sprite *)mem_alloc(sizeof(M4sprite), "mouse sprite")) == nullptr) { return false; } mouseSprite->x = 0; mouseSprite->y = 0; mouseSprite->w = 32; mouseSprite->h = 32; mouseSprite->xOffset = 0; mouseSprite->yOffset = 0; mouseSprite->encoding = 0; mouseSprite->data = GetMousePicture(); mouseSprite->sourceHandle = nullptr; mouseSprite->sourceOffset = 0; _G(mouseX1offset) = mouseSprite->xOffset; _G(mouseY1offset) = mouseSprite->yOffset; _G(mouseX2offset) = mouseSprite->w - _G(mouseX1offset) - 1; _G(mouseY2offset) = mouseSprite->h - _G(mouseY1offset) - 1; } if ((_G(mouseScreenSource) = (transSprite *)mem_alloc(sizeof(transSprite), "mouse transSprite")) == nullptr) { return false; } _G(mouseScreenSource)->srcSprite = mouseSprite; _G(mouseScreenSource)->scrnBuffer = &_G(mouseBuffer); if ((_G(mouseScreen) = vmng_screen_create(0, 0, mouseSprite->w - 1, mouseSprite->h - 1, SCRN_TRANS, SF_MOUSE | SF_OFFSCRN | SF_TRANSPARENT, (void *)_G(mouseScreenSource), transShow, nullptr)) == nullptr) { return false; } _G(mouseSeriesHandle) = nullptr; _G(mouseSeriesOffset) = 0; _G(mouseSeriesPalOffset) = 0; return true; } void gui_mouse_shutdown() { mem_free(_G(mouseSprite)); gr_buffer_free(&_G(mouseBuffer)); mem_free((void *)_G(mouseScreenSource)); _G(mouseSeriesHandle) = nullptr; _G(mouseSeriesOffset) = 0; _G(mouseSeriesPalOffset) = 0; } void transShow(void *s, void *r, void *b, int32 destX, int32 destY) { ScreenContext *myScreen = (ScreenContext *)s; matte *myRectList = (matte *)r; Buffer *destBuffer = (Buffer *)b; ScreenContext *tempScreen; transSprite *mySource; M4sprite *mySprite; Buffer *myBuff; Buffer drawSpriteBuff; DrawRequest spriteDrawReq; matte *myMatte, tempMatte; RectList *updateList, *updateRect; RectList *newUpdateList; uint8 *rowPtr, *destPtr; int32 i, j; // Parameter verification if (!myScreen) return; mySource = (transSprite *)(myScreen->scrnContent); if (!mySource) return; myBuff = (Buffer *)(mySource->scrnBuffer); if (!myBuff) return; mySprite = mySource->srcSprite; if (!mySprite) return; // If no destBuffer, then draw directly to video if (!destBuffer) { tempMatte.nextMatte = nullptr; // Loop through the dirty matte list myMatte = myRectList; while (myMatte) { // Create an updateRectList to catch the black areas afterwards updateList = vmng_CreateNewRect(myMatte->x1, myMatte->y1, myMatte->x2, myMatte->y2); updateList->prev = nullptr; updateList->next = nullptr; // Now loop through all the screens behind myScreen tempScreen = myScreen->behind; while (tempScreen && updateList) { // Duplicate the updateList newUpdateList = vmng_DuplicateRectList(updateList); // Loop through the updateList updateRect = updateList; while (updateRect) { // See if it intersects tempMatte.x1 = imath_max(updateRect->x1, tempScreen->x1); tempMatte.y1 = imath_max(updateRect->y1, tempScreen->y1); tempMatte.x2 = imath_min(updateRect->x2, tempScreen->x2); tempMatte.y2 = imath_min(updateRect->y2, tempScreen->y2); if (tempScreen->redraw && (tempMatte.x1 <= tempMatte.x2) && (tempMatte.y1 <= tempMatte.y2)) { // Draw the intersected part of tempScreen onto myBuffer (tempScreen->redraw)(tempScreen, (void *)&tempMatte, myBuff, tempMatte.x1 - myScreen->x1, tempMatte.y1 - myScreen->y1); // Remove that rectangle from the update list vmng_RemoveRectFromRectList(&newUpdateList, tempMatte.x1, tempMatte.y1, tempMatte.x2, tempMatte.y2); } // Get the next updateRect updateRect = updateRect->next; } // The newUpdateList now contains all the pieces not covered by tempScreen; // turf the update list, and replace it with the newupdateList vmng_DisposeRectList(&updateList); updateList = newUpdateList; // Now get the next screen tempScreen = tempScreen->behind; } // Now we've gone through all the screens, whatever is left in the updateList should be filled in with black gr_color_set(__BLACK); updateRect = updateList; while (updateRect) { gr_buffer_rect_fill(myBuff, updateRect->x1 - myScreen->x1, updateRect->y1 - myScreen->y1, updateRect->x2 - updateRect->x1 + 1, updateRect->y2 - updateRect->y1 + 1); updateRect = updateRect->next; } // Now dispose of the updateList vmng_DisposeRectList(&updateList); // And finally, get the next matte myMatte = myMatte->nextMatte; } // Now myBuff should contain a copy of everything on the screen, except the actual contents of this transparent screen // Now would be the time to draw the contents if (mySprite->sourceHandle) { HLock(mySprite->sourceHandle); mySprite->data = (uint8 *)((intptr)*(mySprite->sourceHandle) + mySprite->sourceOffset); drawSpriteBuff.w = mySprite->w; drawSpriteBuff.stride = mySprite->w; drawSpriteBuff.h = mySprite->h; drawSpriteBuff.encoding = (mySprite->encoding) & (uint8)0x7f; drawSpriteBuff.data = mySprite->data; spriteDrawReq.Src = &drawSpriteBuff; spriteDrawReq.Dest = myBuff; spriteDrawReq.x = 0; spriteDrawReq.y = 0; spriteDrawReq.scaleX = 100; spriteDrawReq.scaleY = 100; spriteDrawReq.srcDepth = 0; spriteDrawReq.depthCode = nullptr; spriteDrawReq.Pal = nullptr; spriteDrawReq.ICT = nullptr; gr_sprite_draw(&spriteDrawReq); // Unlock the handle HUnLock(mySprite->sourceHandle); } else if (mySprite->data) { // Else the data for the transparent sprite is stored directly in mySprite->data // Loop through the rows for (j = 0; (j < mySprite->h) && (j < myBuff->h); j++) { // Set the rowPtr and the destPtr rowPtr = mySprite->data + (j * mySprite->w); destPtr = myBuff->data + (j * myBuff->stride); // Loop through the columns for (i = 0; (i < mySprite->w) && (i < myBuff->w); i++) { if (*rowPtr) { *destPtr = *rowPtr; } destPtr++; rowPtr++; } } } // Now dump the matte list out to video myMatte = myRectList; while (myMatte) { vmng_refresh_video(myMatte->x1, myMatte->y1, myMatte->x1 - myScreen->x1, myMatte->y1 - myScreen->y1, myMatte->x2 - myScreen->x1, myMatte->y2 - myScreen->y1, myBuff); myMatte = myMatte->nextMatte; } } else { // Else draw to the dest buffer myMatte = myRectList; while (myMatte) { gr_buffer_rect_copy_2(myBuff, destBuffer, myMatte->x1 - myScreen->x1, myMatte->y1 - myScreen->y1, destX, destY, myMatte->x2 - myMatte->x1 + 1, myMatte->y2 - myMatte->y1 + 1); myMatte = myMatte->nextMatte; } } } bool mouse_set_sprite(int32 spriteNum) { M4sprite *tempSprite; int32 minX, minY, maxX, maxY; if (_G(mouseIsLocked)) { _G(newMouseNum) = spriteNum; return true; } if (spriteNum == _G(currMouseNum)) { return true; } if (!_G(mouseSeriesHandle) || !*_G(mouseSeriesHandle)) return false; minX = _G(oldX) - _G(mouseX1offset); minY = _G(oldY) - _G(mouseY1offset); maxX = _G(oldX) + _G(mouseX2offset); maxY = _G(oldY) + _G(mouseY2offset); if ((tempSprite = CreateSprite(_G(mouseSeriesHandle), _G(mouseSeriesOffset), spriteNum, _G(mouseSprite), nullptr)) == nullptr) return false; _G(mouseSprite) = tempSprite; _G(mouseX1offset) = _G(mouseSprite)->xOffset; _G(mouseY1offset) = _G(mouseSprite)->yOffset; _G(mouseX2offset) = _G(mouseSprite)->w - _G(mouseX1offset) - 1; _G(mouseY2offset) = _G(mouseSprite)->h - _G(mouseY1offset) - 1; if (_G(mouseX) - _G(mouseX1offset) < minX) minX = _G(mouseX) - _G(mouseX1offset); if (_G(mouseY) - _G(mouseY1offset) < minY) minY = _G(mouseY) - _G(mouseY1offset); if (_G(mouseX) + _G(mouseX2offset) > maxX) maxX = _G(mouseX) + _G(mouseX2offset); if (_G(mouseY) + _G(mouseY2offset) > maxY) maxY = _G(mouseY) + _G(mouseY2offset); gui_mouse_refresh(); _G(currMouseNum) = spriteNum; return true; } void gui_mouse_refresh() { MoveScreenAbs(_G(mouseScreen), _G(mouseX) - _G(mouseX1offset), _G(mouseY) - _G(mouseY1offset)); } void mouse_hide() { vmng_screen_hide(_G(mouseScreenSource)); } void mouse_show() { vmng_screen_show(_G(mouseScreenSource)); } void mouse_lock_sprite(int32 mouseNum) { _G(mouseIsLocked) = false; mouse_set_sprite(mouseNum); _G(mouseIsLocked) = true; } void mouse_unlock_sprite() { _G(mouseIsLocked) = false; mouse_set_sprite(_G(newMouseNum)); } } // End of namespace M4