diff --git a/minui/graphics.cpp b/minui/graphics.cpp index f25694ab..597f2cea 100644 --- a/minui/graphics.cpp +++ b/minui/graphics.cpp @@ -450,6 +450,10 @@ void gr_fb_blank(bool blank) { gr_backend->Blank(blank); } +void gr_fb_blank(bool blank, int index) { + gr_backend->Blank(blank, static_cast(index)); +} + void gr_rotate(GRRotation rot) { rotation = rot; } diff --git a/minui/graphics.h b/minui/graphics.h index 3c45a406..5408c93e 100644 --- a/minui/graphics.h +++ b/minui/graphics.h @@ -21,6 +21,12 @@ class MinuiBackend { public: + enum DrmConnector { + DRM_MAIN = 0, + DRM_SEC, + DRM_MAX, + }; + // Initializes the backend and returns a GRSurface* to draw into. virtual GRSurface* Init() = 0; @@ -28,9 +34,12 @@ class MinuiBackend { // be displayed, and returns a new drawing surface. virtual GRSurface* Flip() = 0; - // Blank (or unblank) the screen. + // Blank (or unblank) the default screen. virtual void Blank(bool) = 0; + // Blank (or unblank) the specific screen. + virtual void Blank(bool blank, DrmConnector index) = 0; + // Device cleanup when drawing is done. virtual ~MinuiBackend() {}; }; diff --git a/minui/graphics_drm.cpp b/minui/graphics_drm.cpp index 9d31ff7a..c5570227 100644 --- a/minui/graphics_drm.cpp +++ b/minui/graphics_drm.cpp @@ -153,22 +153,50 @@ void MinuiBackendDrm::DrmDisableCrtc(int drm_fd, drmModeCrtc* crtc) { } bool MinuiBackendDrm::DrmEnableCrtc(int drm_fd, drmModeCrtc* crtc, - const std::unique_ptr& surface) { + const std::unique_ptr& surface, + uint32_t* connector_id) { if (drmModeSetCrtc(drm_fd, crtc->crtc_id, surface->fb_id, 0, 0, // x,y - &main_monitor_connector->connector_id, - 1, // connector_count - &main_monitor_crtc->mode) != 0) { - perror("Failed to drmModeSetCrtc"); + connector_id, 1, // connector_count + &crtc->mode) != 0) { + fprintf(stderr, "Failed to drmModeSetCrtc(%d)\n", *connector_id); return false; } + return true; } void MinuiBackendDrm::Blank(bool blank) { + Blank(blank, DRM_MAIN); +} + +void MinuiBackendDrm::Blank(bool blank, DrmConnector index) { + const auto* drmInterface = &drm[DRM_MAIN]; + + switch (index) { + case DRM_MAIN: + drmInterface = &drm[DRM_MAIN]; + break; + case DRM_SEC: + drmInterface = &drm[DRM_SEC]; + break; + default: + fprintf(stderr, "Invalid index: %d\n", index); + return; + } + + if (!drmInterface->monitor_connector) { + fprintf(stderr, "Unsupported. index = %d\n", index); + return; + } + if (blank) { - DrmDisableCrtc(drm_fd, main_monitor_crtc); + DrmDisableCrtc(drm_fd, drmInterface->monitor_crtc); } else { - DrmEnableCrtc(drm_fd, main_monitor_crtc, GRSurfaceDrms[current_buffer]); + DrmEnableCrtc(drm_fd, drmInterface->monitor_crtc, + drmInterface->GRSurfaceDrms[drmInterface->current_buffer], + &drmInterface->monitor_connector->connector_id); + + active_display = index; } } @@ -210,18 +238,21 @@ static drmModeCrtc* find_crtc_for_connector(int fd, drmModeRes* resources, return nullptr; } -static drmModeConnector* find_used_connector_by_type(int fd, drmModeRes* resources, unsigned type) { +std::vector find_used_connector_by_type(int fd, drmModeRes* resources, + unsigned type) { + std::vector drmConnectors; for (int i = 0; i < resources->count_connectors; i++) { drmModeConnector* connector = drmModeGetConnector(fd, resources->connectors[i]); if (connector) { if ((connector->connector_type == type) && (connector->connection == DRM_MODE_CONNECTED) && (connector->count_modes > 0)) { - return connector; + drmConnectors.push_back(connector); + } else { + drmModeFreeConnector(connector); } - drmModeFreeConnector(connector); } } - return nullptr; + return drmConnectors; } static drmModeConnector* find_first_connected_connector(int fd, drmModeRes* resources) { @@ -239,8 +270,7 @@ static drmModeConnector* find_first_connected_connector(int fd, drmModeRes* reso return nullptr; } -drmModeConnector* MinuiBackendDrm::FindMainMonitor(int fd, drmModeRes* resources, - uint32_t* mode_index) { +bool MinuiBackendDrm::FindAndSetMonitor(int fd, drmModeRes* resources) { /* Look for LVDS/eDP/DSI connectors. Those are the main screens. */ static constexpr unsigned kConnectorPriority[] = { DRM_MODE_CONNECTOR_LVDS, @@ -248,37 +278,41 @@ drmModeConnector* MinuiBackendDrm::FindMainMonitor(int fd, drmModeRes* resources DRM_MODE_CONNECTOR_DSI, }; - drmModeConnector* main_monitor_connector = nullptr; - unsigned i = 0; - do { - main_monitor_connector = find_used_connector_by_type(fd, resources, kConnectorPriority[i]); - i++; - } while (!main_monitor_connector && i < arraysize(kConnectorPriority)); - - /* If we didn't find a connector, grab the first one that is connected. */ - if (!main_monitor_connector) { - main_monitor_connector = find_first_connected_connector(fd, resources); - } - - /* If we still didn't find a connector, give up and return. */ - if (!main_monitor_connector) return nullptr; - - for (int modes = 0; modes < main_monitor_connector->count_modes; modes++) { - printf("Display Mode %d resolution: %d x %d @ %d FPS\n", modes, - main_monitor_connector->modes[modes].hdisplay, - main_monitor_connector->modes[modes].vdisplay, - main_monitor_connector->modes[modes].vrefresh); - } - *mode_index = 0; - for (int modes = 0; modes < main_monitor_connector->count_modes; modes++) { - if (main_monitor_connector->modes[modes].type & DRM_MODE_TYPE_PREFERRED) { - printf("Choosing display mode #%d\n", modes); - *mode_index = modes; - break; + std::vector drmConnectors; + for (int i = 0; i < arraysize(kConnectorPriority) && drmConnectors.size() < DRM_MAX; i++) { + auto connectors = find_used_connector_by_type(fd, resources, kConnectorPriority[i]); + for (auto connector : connectors) { + drmConnectors.push_back(connector); + if (drmConnectors.size() >= DRM_MAX) break; } } - return main_monitor_connector; + /* If we didn't find a connector, grab the first one that is connected. */ + if (drmConnectors.empty()) { + drmModeConnector* connector = find_first_connected_connector(fd, resources); + if (connector) { + drmConnectors.push_back(connector); + } + } + + for (int drm_index = 0; drm_index < drmConnectors.size(); drm_index++) { + drm[drm_index].monitor_connector = drmConnectors[drm_index]; + + drm[drm_index].selected_mode = 0; + for (int modes = 0; modes < drmConnectors[drm_index]->count_modes; modes++) { + printf("Display Mode %d resolution: %d x %d @ %d FPS\n", modes, + drmConnectors[drm_index]->modes[modes].hdisplay, + drmConnectors[drm_index]->modes[modes].vdisplay, + drmConnectors[drm_index]->modes[modes].vrefresh); + if (drmConnectors[drm_index]->modes[modes].type & DRM_MODE_TYPE_PREFERRED) { + printf("Choosing display mode #%d\n", modes); + drm[drm_index].selected_mode = modes; + break; + } + } + } + + return drmConnectors.size() > 0; } void MinuiBackendDrm::DisableNonMainCrtcs(int fd, drmModeRes* resources, drmModeCrtc* main_crtc) { @@ -329,46 +363,49 @@ GRSurface* MinuiBackendDrm::Init() { return nullptr; } - uint32_t selected_mode; - main_monitor_connector = FindMainMonitor(drm_fd, res, &selected_mode); - if (!main_monitor_connector) { - fprintf(stderr, "Failed to find main_monitor_connector\n"); + if (!FindAndSetMonitor(drm_fd, res)) { + fprintf(stderr, "Failed to find main monitor_connector\n"); drmModeFreeResources(res); - close(drm_fd); return nullptr; } - main_monitor_crtc = find_crtc_for_connector(drm_fd, res, main_monitor_connector); - if (!main_monitor_crtc) { - fprintf(stderr, "Failed to find main_monitor_crtc\n"); - drmModeFreeResources(res); - close(drm_fd); - return nullptr; + for (int i = 0; i < DRM_MAX; i++) { + if (drm[i].monitor_connector) { + drm[i].monitor_crtc = find_crtc_for_connector(drm_fd, res, drm[i].monitor_connector); + if (!drm[i].monitor_crtc) { + fprintf(stderr, "Failed to find monitor_crtc, drm index=%d\n", i); + drmModeFreeResources(res); + return nullptr; + } + + drm[i].monitor_crtc->mode = drm[i].monitor_connector->modes[drm[i].selected_mode]; + + int width = drm[i].monitor_crtc->mode.hdisplay; + int height = drm[i].monitor_crtc->mode.vdisplay; + + drm[i].GRSurfaceDrms[0] = GRSurfaceDrm::Create(drm_fd, width, height); + drm[i].GRSurfaceDrms[1] = GRSurfaceDrm::Create(drm_fd, width, height); + if (!drm[i].GRSurfaceDrms[0] || !drm[i].GRSurfaceDrms[1]) { + fprintf(stderr, "Failed to create GRSurfaceDrm, drm index=%d\n", i); + drmModeFreeResources(res); + return nullptr; + } + + drm[i].current_buffer = 0; + } } - DisableNonMainCrtcs(drm_fd, res, main_monitor_crtc); - - main_monitor_crtc->mode = main_monitor_connector->modes[selected_mode]; - - int width = main_monitor_crtc->mode.hdisplay; - int height = main_monitor_crtc->mode.vdisplay; + DisableNonMainCrtcs(drm_fd, res, drm[DRM_MAIN].monitor_crtc); drmModeFreeResources(res); - GRSurfaceDrms[0] = GRSurfaceDrm::Create(drm_fd, width, height); - GRSurfaceDrms[1] = GRSurfaceDrm::Create(drm_fd, width, height); - if (!GRSurfaceDrms[0] || !GRSurfaceDrms[1]) { - return nullptr; - } - - current_buffer = 0; - // We will likely encounter errors in the backend functions (i.e. Flip) if EnableCrtc fails. - if (!DrmEnableCrtc(drm_fd, main_monitor_crtc, GRSurfaceDrms[1])) { + if (!DrmEnableCrtc(drm_fd, drm[DRM_MAIN].monitor_crtc, drm[DRM_MAIN].GRSurfaceDrms[1], + &drm[DRM_MAIN].monitor_connector->connector_id)) { return nullptr; } - return GRSurfaceDrms[0].get(); + return drm[DRM_MAIN].GRSurfaceDrms[0].get(); } static void page_flip_complete(__unused int fd, @@ -380,10 +417,19 @@ static void page_flip_complete(__unused int fd, } GRSurface* MinuiBackendDrm::Flip() { + GRSurface* surface = NULL; + DrmInterface* current_drm = &drm[active_display]; bool ongoing_flip = true; - if (drmModePageFlip(drm_fd, main_monitor_crtc->crtc_id, GRSurfaceDrms[current_buffer]->fb_id, + + if (!current_drm->monitor_connector) { + fprintf(stderr, "Unsupported. active_display = %d\n", active_display); + return nullptr; + } + + if (drmModePageFlip(drm_fd, current_drm->monitor_crtc->crtc_id, + current_drm->GRSurfaceDrms[current_drm->current_buffer]->fb_id, DRM_MODE_PAGE_FLIP_EVENT, &ongoing_flip) != 0) { - perror("Failed to drmModePageFlip"); + fprintf(stderr, "Failed to drmModePageFlip, active_display=%d", active_display); return nullptr; } @@ -409,14 +455,19 @@ GRSurface* MinuiBackendDrm::Flip() { } } - current_buffer = 1 - current_buffer; - return GRSurfaceDrms[current_buffer].get(); + current_drm->current_buffer = 1 - current_drm->current_buffer; + surface = current_drm->GRSurfaceDrms[current_drm->current_buffer].get(); + return surface; } MinuiBackendDrm::~MinuiBackendDrm() { - DrmDisableCrtc(drm_fd, main_monitor_crtc); - drmModeFreeCrtc(main_monitor_crtc); - drmModeFreeConnector(main_monitor_connector); + for (int i = 0; i < DRM_MAX; i++) { + if (drm[i].monitor_connector) { + DrmDisableCrtc(drm_fd, drm[i].monitor_crtc); + drmModeFreeCrtc(drm[i].monitor_crtc); + drmModeFreeConnector(drm[i].monitor_connector); + } + } close(drm_fd); drm_fd = -1; } diff --git a/minui/graphics_drm.h b/minui/graphics_drm.h index 57ba39b8..fe3beaff 100644 --- a/minui/graphics_drm.h +++ b/minui/graphics_drm.h @@ -59,16 +59,23 @@ class MinuiBackendDrm : public MinuiBackend { GRSurface* Init() override; GRSurface* Flip() override; void Blank(bool) override; + void Blank(bool blank, DrmConnector index) override; private: void DrmDisableCrtc(int drm_fd, drmModeCrtc* crtc); - bool DrmEnableCrtc(int drm_fd, drmModeCrtc* crtc, const std::unique_ptr& surface); + bool DrmEnableCrtc(int drm_fd, drmModeCrtc* crtc, const std::unique_ptr& surface, + uint32_t* conntcors); void DisableNonMainCrtcs(int fd, drmModeRes* resources, drmModeCrtc* main_crtc); - drmModeConnector* FindMainMonitor(int fd, drmModeRes* resources, uint32_t* mode_index); + bool FindAndSetMonitor(int fd, drmModeRes* resources); + + struct DrmInterface { + std::unique_ptr GRSurfaceDrms[2]; + int current_buffer{ 0 }; + drmModeCrtc* monitor_crtc{ nullptr }; + drmModeConnector* monitor_connector{ nullptr }; + uint32_t selected_mode{ 0 }; + } drm[DRM_MAX]; - std::unique_ptr GRSurfaceDrms[2]; - int current_buffer{ 0 }; - drmModeCrtc* main_monitor_crtc{ nullptr }; - drmModeConnector* main_monitor_connector{ nullptr }; int drm_fd{ -1 }; + DrmConnector active_display = DRM_MAIN; }; diff --git a/minui/graphics_fbdev.cpp b/minui/graphics_fbdev.cpp index 0d0fabce..1cb0c0ab 100644 --- a/minui/graphics_fbdev.cpp +++ b/minui/graphics_fbdev.cpp @@ -43,6 +43,10 @@ void MinuiBackendFbdev::Blank(bool blank) { if (ret < 0) perror("ioctl(): blank"); } +void MinuiBackendFbdev::Blank(bool blank, DrmConnector index) { + fprintf(stderr, "Unsupported multiple connectors, blank = %d, index = %d\n", blank, index); +} + void MinuiBackendFbdev::SetDisplayedFramebuffer(size_t n) { if (n > 1 || !double_buffered) return; diff --git a/minui/graphics_fbdev.h b/minui/graphics_fbdev.h index 596ba74e..7e193c4f 100644 --- a/minui/graphics_fbdev.h +++ b/minui/graphics_fbdev.h @@ -56,6 +56,7 @@ class MinuiBackendFbdev : public MinuiBackend { GRSurface* Init() override; GRSurface* Flip() override; void Blank(bool) override; + void Blank(bool blank, DrmConnector index) override; private: void SetDisplayedFramebuffer(size_t n); diff --git a/minui/include/minui/minui.h b/minui/include/minui/minui.h index 5470457e..6a762b6a 100644 --- a/minui/include/minui/minui.h +++ b/minui/include/minui/minui.h @@ -127,6 +127,7 @@ int gr_fb_height(); void gr_flip(); void gr_fb_blank(bool blank); +void gr_fb_blank(bool blank, int index); // Clears entire surface to current color. void gr_clear();