dingusppc/devices/video/display_sdl.cpp
joevt 7b4d513e22 videoctrl: Add change resolution support.
PDM defaults to 640x480.
If you set --mon_id to MacRGB12in then it would draw 512x384 inside a 640x480 window.
If you set --mon_id to Multiscan20in then it would try to draw 832x624 inside a 640x480 window and crash.
If you set the Monitors control panel to switch multiscan display from 640x480 to 832x624 and  restart then it would crash.
Now it will correctly change the window size every time the mode changes.
2024-03-06 21:44:10 -07:00

167 lines
5.2 KiB
C++

/*
DingusPPC - The Experimental PowerPC Macintosh emulator
Copyright (C) 2018-23 divingkatae and maximum
(theweirdo) spatium
(Contact divingkatae#1017 or powermax#2286 on Discord for more info)
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 <https://www.gnu.org/licenses/>.
*/
#include <devices/video/display.h>
#include <SDL.h>
#include <loguru.hpp>
class Display::Impl {
public:
bool resizing = false;
uint32_t disp_wnd_id = 0;
SDL_Window* display_wnd = 0;
SDL_Renderer* renderer = 0;
SDL_Texture* disp_texture = 0;
SDL_Texture* cursor_texture = 0;
SDL_Rect cursor_rect; // destination rectangle for cursor drawing
};
Display::Display(): impl(std::make_unique<Impl>()) {
}
Display::~Display() {
if (impl->cursor_texture)
SDL_DestroyTexture(impl->cursor_texture);
if (impl->disp_texture)
SDL_DestroyTexture(impl->disp_texture);
if (impl->renderer)
SDL_DestroyRenderer(impl->renderer);
if (impl->display_wnd)
SDL_DestroyWindow(impl->display_wnd);
}
bool Display::configure(int width, int height) {
bool is_initialization = false;
if (!impl->display_wnd) { // create display window
impl->display_wnd = SDL_CreateWindow(
"DingusPPC Display",
SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED,
width, height,
SDL_WINDOW_OPENGL
);
impl->disp_wnd_id = SDL_GetWindowID(impl->display_wnd);
if (impl->display_wnd == NULL)
ABORT_F("Display: SDL_CreateWindow failed with %s", SDL_GetError());
impl->renderer = SDL_CreateRenderer(impl->display_wnd, -1, SDL_RENDERER_ACCELERATED);
if (impl->renderer == NULL)
ABORT_F("Display: SDL_CreateRenderer failed with %s", SDL_GetError());
is_initialization = true;
} else { // resize display window
SDL_SetWindowSize(impl->display_wnd, width, height);
}
if (impl->disp_texture)
SDL_DestroyTexture(impl->disp_texture);
impl->disp_texture = SDL_CreateTexture(
impl->renderer,
SDL_PIXELFORMAT_ARGB8888,
SDL_TEXTUREACCESS_STREAMING,
width, height
);
if (impl->disp_texture == NULL)
ABORT_F("Display: SDL_CreateTexture failed with %s", SDL_GetError());
return is_initialization;
}
void Display::handle_events(const WindowEvent& wnd_event) {
if (wnd_event.sub_type == SDL_WINDOWEVENT_SIZE_CHANGED &&
wnd_event.window_id == impl->disp_wnd_id)
impl->resizing = false;
}
void Display::blank() {
SDL_SetRenderDrawColor(impl->renderer, 0, 0, 0, 255);
SDL_RenderClear(impl->renderer);
SDL_RenderPresent(impl->renderer);
}
void Display::update(std::function<void(uint8_t *dst_buf, int dst_pitch)> convert_fb_cb,
std::function<void(uint8_t *dst_buf, int dst_pitch)> cursor_ovl_cb,
bool draw_hw_cursor, int cursor_x, int cursor_y) {
if (impl->resizing)
return;
uint8_t* dst_buf;
int dst_pitch;
SDL_LockTexture(impl->disp_texture, NULL, (void **)&dst_buf, &dst_pitch);
// texture update callback to get ARGB data from guest framebuffer
convert_fb_cb(dst_buf, dst_pitch);
// overlay cursor data if requested
if (cursor_ovl_cb != nullptr)
cursor_ovl_cb(dst_buf, dst_pitch);
SDL_UnlockTexture(impl->disp_texture);
SDL_RenderClear(impl->renderer);
SDL_RenderCopy(impl->renderer, impl->disp_texture, NULL, NULL);
// draw HW cursor if enabled
if (draw_hw_cursor) {
impl->cursor_rect.x = cursor_x;
impl->cursor_rect.y = cursor_y;
SDL_RenderCopy(impl->renderer, impl->cursor_texture, NULL, &impl->cursor_rect);
}
SDL_RenderPresent(impl->renderer);
}
void Display::setup_hw_cursor(std::function<void(uint8_t *dst_buf, int dst_pitch)> draw_hw_cursor,
int cursor_width, int cursor_height) {
uint8_t* dst_buf;
int dst_pitch;
if (impl->cursor_texture)
SDL_DestroyTexture(impl->cursor_texture);
impl->cursor_texture = SDL_CreateTexture(
impl->renderer,
SDL_PIXELFORMAT_ARGB8888,
SDL_TEXTUREACCESS_STREAMING,
cursor_width, cursor_height
);
if (impl->cursor_texture == NULL)
ABORT_F("SDL_CreateTexture for HW cursor failed with %s", SDL_GetError());
SDL_LockTexture(impl->cursor_texture, NULL, (void **)&dst_buf, &dst_pitch);
SDL_SetTextureBlendMode(impl->cursor_texture, SDL_BLENDMODE_BLEND);
draw_hw_cursor(dst_buf, dst_pitch);
SDL_UnlockTexture(impl->cursor_texture);
impl->cursor_rect.x = 0;
impl->cursor_rect.y = 0;
impl->cursor_rect.w = cursor_width;
impl->cursor_rect.h = cursor_height;
}