diff options
-rw-r--r-- | minui/graphics_adf.cpp | 348 | ||||
-rw-r--r-- | minui/graphics_drm.cpp | 648 | ||||
-rw-r--r-- | minui/graphics_fbdev.cpp | 268 |
3 files changed, 583 insertions, 681 deletions
diff --git a/minui/graphics_adf.cpp b/minui/graphics_adf.cpp index 9e262b044..9ab0b06bf 100644 --- a/minui/graphics_adf.cpp +++ b/minui/graphics_adf.cpp @@ -16,14 +16,10 @@ #include <errno.h> #include <fcntl.h> -#include <stdbool.h> #include <stdio.h> #include <stdlib.h> -#include <string.h> -#include <unistd.h> - -#include <sys/cdefs.h> #include <sys/mman.h> +#include <unistd.h> #include <adf/adf.h> #include <sync/sync.h> @@ -31,243 +27,213 @@ #include "graphics.h" struct adf_surface_pdata { - GRSurface base; - int fence_fd; - int fd; - __u32 offset; - __u32 pitch; + GRSurface base; + int fence_fd; + int fd; + __u32 offset; + __u32 pitch; }; struct adf_pdata { - minui_backend base; - int intf_fd; - adf_id_t eng_id; - __u32 format; + minui_backend base; + int intf_fd; + adf_id_t eng_id; + __u32 format; - adf_device dev; + adf_device dev; - unsigned int current_surface; - unsigned int n_surfaces; - adf_surface_pdata surfaces[2]; + unsigned int current_surface; + unsigned int n_surfaces; + adf_surface_pdata surfaces[2]; }; -static GRSurface* adf_flip(minui_backend *backend); -static void adf_blank(minui_backend *backend, bool blank); - -static int adf_surface_init(adf_pdata *pdata, drm_mode_modeinfo *mode, adf_surface_pdata *surf) { - memset(surf, 0, sizeof(*surf)); - - surf->fence_fd = -1; - surf->fd = adf_interface_simple_buffer_alloc(pdata->intf_fd, mode->hdisplay, - mode->vdisplay, pdata->format, &surf->offset, &surf->pitch); - if (surf->fd < 0) - return surf->fd; - - surf->base.width = mode->hdisplay; - surf->base.height = mode->vdisplay; - surf->base.row_bytes = surf->pitch; - surf->base.pixel_bytes = (pdata->format == DRM_FORMAT_RGB565) ? 2 : 4; - - surf->base.data = static_cast<uint8_t*>(mmap(NULL, - surf->pitch * surf->base.height, PROT_WRITE, - MAP_SHARED, surf->fd, surf->offset)); - if (surf->base.data == MAP_FAILED) { - close(surf->fd); - return -errno; - } +static GRSurface* adf_flip(minui_backend* backend); +static void adf_blank(minui_backend* backend, bool blank); + +static int adf_surface_init(adf_pdata* pdata, drm_mode_modeinfo* mode, adf_surface_pdata* surf) { + *surf = {}; + surf->fence_fd = -1; + surf->fd = adf_interface_simple_buffer_alloc(pdata->intf_fd, mode->hdisplay, mode->vdisplay, + pdata->format, &surf->offset, &surf->pitch); + if (surf->fd < 0) { + return surf->fd; + } + + surf->base.width = mode->hdisplay; + surf->base.height = mode->vdisplay; + surf->base.row_bytes = surf->pitch; + surf->base.pixel_bytes = (pdata->format == DRM_FORMAT_RGB565) ? 2 : 4; + + surf->base.data = static_cast<uint8_t*>(mmap(nullptr, surf->pitch * surf->base.height, PROT_WRITE, + MAP_SHARED, surf->fd, surf->offset)); + if (surf->base.data == MAP_FAILED) { + int saved_errno = errno; + close(surf->fd); + return -saved_errno; + } - return 0; + return 0; } -static int adf_interface_init(adf_pdata *pdata) -{ - adf_interface_data intf_data; - int ret = 0; - int err; - - err = adf_get_interface_data(pdata->intf_fd, &intf_data); - if (err < 0) - return err; - - err = adf_surface_init(pdata, &intf_data.current_mode, &pdata->surfaces[0]); - if (err < 0) { - fprintf(stderr, "allocating surface 0 failed: %s\n", strerror(-err)); - ret = err; - goto done; - } - - err = adf_surface_init(pdata, &intf_data.current_mode, - &pdata->surfaces[1]); - if (err < 0) { - fprintf(stderr, "allocating surface 1 failed: %s\n", strerror(-err)); - memset(&pdata->surfaces[1], 0, sizeof(pdata->surfaces[1])); - pdata->n_surfaces = 1; - } else { - pdata->n_surfaces = 2; - } +static int adf_interface_init(adf_pdata* pdata) { + adf_interface_data intf_data; + int err = adf_get_interface_data(pdata->intf_fd, &intf_data); + if (err < 0) return err; + + int ret = 0; + err = adf_surface_init(pdata, &intf_data.current_mode, &pdata->surfaces[0]); + if (err < 0) { + fprintf(stderr, "allocating surface 0 failed: %s\n", strerror(-err)); + ret = err; + goto done; + } + + err = adf_surface_init(pdata, &intf_data.current_mode, &pdata->surfaces[1]); + if (err < 0) { + fprintf(stderr, "allocating surface 1 failed: %s\n", strerror(-err)); + pdata->surfaces[1] = {}; + pdata->n_surfaces = 1; + } else { + pdata->n_surfaces = 2; + } done: - adf_free_interface_data(&intf_data); - return ret; + adf_free_interface_data(&intf_data); + return ret; } -static int adf_device_init(adf_pdata *pdata, adf_device *dev) -{ - adf_id_t intf_id; - int intf_fd; - int err; +static int adf_device_init(adf_pdata* pdata, adf_device* dev) { + adf_id_t intf_id; + int err = adf_find_simple_post_configuration(dev, &pdata->format, 1, &intf_id, &pdata->eng_id); + if (err < 0) return err; - err = adf_find_simple_post_configuration(dev, &pdata->format, 1, &intf_id, - &pdata->eng_id); - if (err < 0) - return err; + err = adf_device_attach(dev, pdata->eng_id, intf_id); + if (err < 0 && err != -EALREADY) return err; - err = adf_device_attach(dev, pdata->eng_id, intf_id); - if (err < 0 && err != -EALREADY) - return err; + pdata->intf_fd = adf_interface_open(dev, intf_id, O_RDWR); + if (pdata->intf_fd < 0) return pdata->intf_fd; - pdata->intf_fd = adf_interface_open(dev, intf_id, O_RDWR); - if (pdata->intf_fd < 0) - return pdata->intf_fd; - - err = adf_interface_init(pdata); - if (err < 0) { - close(pdata->intf_fd); - pdata->intf_fd = -1; - } + err = adf_interface_init(pdata); + if (err < 0) { + close(pdata->intf_fd); + pdata->intf_fd = -1; + } - return err; + return err; } -static GRSurface* adf_init(minui_backend *backend) -{ - adf_pdata *pdata = (adf_pdata *)backend; - adf_id_t *dev_ids = NULL; - ssize_t n_dev_ids, i; - GRSurface* ret; +static GRSurface* adf_init(minui_backend* backend) { + adf_pdata* pdata = reinterpret_cast<adf_pdata*>(backend); #if defined(RECOVERY_ABGR) - pdata->format = DRM_FORMAT_ABGR8888; + pdata->format = DRM_FORMAT_ABGR8888; #elif defined(RECOVERY_BGRA) - pdata->format = DRM_FORMAT_BGRA8888; + pdata->format = DRM_FORMAT_BGRA8888; #elif defined(RECOVERY_RGBX) - pdata->format = DRM_FORMAT_RGBX8888; + pdata->format = DRM_FORMAT_RGBX8888; #else - pdata->format = DRM_FORMAT_RGB565; + pdata->format = DRM_FORMAT_RGB565; #endif - n_dev_ids = adf_devices(&dev_ids); - if (n_dev_ids == 0) { - return NULL; - } else if (n_dev_ids < 0) { - fprintf(stderr, "enumerating adf devices failed: %s\n", - strerror(-n_dev_ids)); - return NULL; - } + adf_id_t* dev_ids = nullptr; + ssize_t n_dev_ids = adf_devices(&dev_ids); + if (n_dev_ids == 0) { + return nullptr; + } else if (n_dev_ids < 0) { + fprintf(stderr, "enumerating adf devices failed: %s\n", strerror(-n_dev_ids)); + return nullptr; + } - pdata->intf_fd = -1; + pdata->intf_fd = -1; - for (i = 0; i < n_dev_ids && pdata->intf_fd < 0; i++) { - - int err = adf_device_open(dev_ids[i], O_RDWR, &pdata->dev); - if (err < 0) { - fprintf(stderr, "opening adf device %u failed: %s\n", dev_ids[i], - strerror(-err)); - continue; - } - - err = adf_device_init(pdata, &pdata->dev); - if (err < 0) { - fprintf(stderr, "initializing adf device %u failed: %s\n", - dev_ids[i], strerror(-err)); - adf_device_close(&pdata->dev); - } + for (ssize_t i = 0; i < n_dev_ids && pdata->intf_fd < 0; i++) { + int err = adf_device_open(dev_ids[i], O_RDWR, &pdata->dev); + if (err < 0) { + fprintf(stderr, "opening adf device %u failed: %s\n", dev_ids[i], strerror(-err)); + continue; } - free(dev_ids); + err = adf_device_init(pdata, &pdata->dev); + if (err < 0) { + fprintf(stderr, "initializing adf device %u failed: %s\n", dev_ids[i], strerror(-err)); + adf_device_close(&pdata->dev); + } + } - if (pdata->intf_fd < 0) - return NULL; + free(dev_ids); - ret = adf_flip(backend); + if (pdata->intf_fd < 0) return nullptr; - adf_blank(backend, true); - adf_blank(backend, false); + GRSurface* ret = adf_flip(backend); - return ret; -} + adf_blank(backend, true); + adf_blank(backend, false); -static void adf_sync(adf_surface_pdata *surf) -{ - unsigned int warningTimeout = 3000; + return ret; +} - if (surf == NULL) - return; +static void adf_sync(adf_surface_pdata* surf) { + static constexpr unsigned int warningTimeout = 3000; - if (surf->fence_fd >= 0){ - int err = sync_wait(surf->fence_fd, warningTimeout); - if (err < 0) - perror("adf sync fence wait error\n"); + if (surf == nullptr) return; - close(surf->fence_fd); - surf->fence_fd = -1; + if (surf->fence_fd >= 0) { + int err = sync_wait(surf->fence_fd, warningTimeout); + if (err < 0) { + perror("adf sync fence wait error\n"); } + + close(surf->fence_fd); + surf->fence_fd = -1; + } } -static GRSurface* adf_flip(minui_backend *backend) -{ - adf_pdata *pdata = (adf_pdata *)backend; - adf_surface_pdata *surf = &pdata->surfaces[pdata->current_surface]; +static GRSurface* adf_flip(minui_backend* backend) { + adf_pdata* pdata = reinterpret_cast<adf_pdata*>(backend); + adf_surface_pdata* surf = &pdata->surfaces[pdata->current_surface]; - int fence_fd = adf_interface_simple_post(pdata->intf_fd, pdata->eng_id, - surf->base.width, surf->base.height, pdata->format, surf->fd, - surf->offset, surf->pitch, -1); - if (fence_fd >= 0) - surf->fence_fd = fence_fd; + int fence_fd = + adf_interface_simple_post(pdata->intf_fd, pdata->eng_id, surf->base.width, surf->base.height, + pdata->format, surf->fd, surf->offset, surf->pitch, -1); + if (fence_fd >= 0) surf->fence_fd = fence_fd; - pdata->current_surface = (pdata->current_surface + 1) % pdata->n_surfaces; - adf_sync(&pdata->surfaces[pdata->current_surface]); - return &pdata->surfaces[pdata->current_surface].base; + pdata->current_surface = (pdata->current_surface + 1) % pdata->n_surfaces; + adf_sync(&pdata->surfaces[pdata->current_surface]); + return &pdata->surfaces[pdata->current_surface].base; } -static void adf_blank(minui_backend *backend, bool blank) -{ - adf_pdata *pdata = (adf_pdata *)backend; - adf_interface_blank(pdata->intf_fd, - blank ? DRM_MODE_DPMS_OFF : DRM_MODE_DPMS_ON); +static void adf_blank(minui_backend* backend, bool blank) { + adf_pdata* pdata = reinterpret_cast<adf_pdata*>(backend); + adf_interface_blank(pdata->intf_fd, blank ? DRM_MODE_DPMS_OFF : DRM_MODE_DPMS_ON); } -static void adf_surface_destroy(adf_surface_pdata *surf) -{ - munmap(surf->base.data, surf->pitch * surf->base.height); - close(surf->fence_fd); - close(surf->fd); +static void adf_surface_destroy(adf_surface_pdata* surf) { + munmap(surf->base.data, surf->pitch * surf->base.height); + close(surf->fence_fd); + close(surf->fd); } -static void adf_exit(minui_backend *backend) -{ - adf_pdata *pdata = (adf_pdata *)backend; - unsigned int i; - - adf_device_close(&pdata->dev); - for (i = 0; i < pdata->n_surfaces; i++) - adf_surface_destroy(&pdata->surfaces[i]); - if (pdata->intf_fd >= 0) - close(pdata->intf_fd); - free(pdata); +static void adf_exit(minui_backend* backend) { + adf_pdata* pdata = reinterpret_cast<adf_pdata*>(backend); + adf_device_close(&pdata->dev); + for (unsigned int i = 0; i < pdata->n_surfaces; i++) { + adf_surface_destroy(&pdata->surfaces[i]); + } + if (pdata->intf_fd >= 0) close(pdata->intf_fd); + free(pdata); } -minui_backend *open_adf() -{ - adf_pdata* pdata = static_cast<adf_pdata*>(calloc(1, sizeof(*pdata))); - if (!pdata) { - perror("allocating adf backend failed"); - return NULL; - } +minui_backend* open_adf() { + adf_pdata* pdata = static_cast<adf_pdata*>(calloc(1, sizeof(*pdata))); + if (!pdata) { + perror("allocating adf backend failed"); + return nullptr; + } + + pdata->base.init = adf_init; + pdata->base.flip = adf_flip; + pdata->base.blank = adf_blank; + pdata->base.exit = adf_exit; - pdata->base.init = adf_init; - pdata->base.flip = adf_flip; - pdata->base.blank = adf_blank; - pdata->base.exit = adf_exit; - return &pdata->base; + return &pdata->base; } diff --git a/minui/graphics_drm.cpp b/minui/graphics_drm.cpp index 199f4d83c..2eeff5800 100644 --- a/minui/graphics_drm.cpp +++ b/minui/graphics_drm.cpp @@ -14,16 +14,14 @@ * limitations under the License. */ -#include <drm_fourcc.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> -#include <string.h> -#include <sys/cdefs.h> -#include <sys/ioctl.h> #include <sys/mman.h> #include <sys/types.h> #include <unistd.h> + +#include <drm_fourcc.h> #include <xf86drm.h> #include <xf86drmMode.h> @@ -33,9 +31,9 @@ #define ARRAY_SIZE(A) (sizeof(A)/sizeof(*(A))) struct drm_surface { - GRSurface base; - uint32_t fb_id; - uint32_t handle; + GRSurface base; + uint32_t fb_id; + uint32_t handle; }; static drm_surface *drm_surfaces[2]; @@ -47,429 +45,377 @@ static drmModeConnector *main_monitor_connector; static int drm_fd = -1; static void drm_disable_crtc(int drm_fd, drmModeCrtc *crtc) { - if (crtc) { - drmModeSetCrtc(drm_fd, crtc->crtc_id, - 0, // fb_id - 0, 0, // x,y - NULL, // connectors - 0, // connector_count - NULL); // mode - } + if (crtc) { + drmModeSetCrtc(drm_fd, crtc->crtc_id, + 0, // fb_id + 0, 0, // x,y + nullptr, // connectors + 0, // connector_count + nullptr); // mode + } } -static void drm_enable_crtc(int drm_fd, drmModeCrtc *crtc, - struct drm_surface *surface) { - int32_t ret; +static void drm_enable_crtc(int drm_fd, drmModeCrtc* crtc, struct drm_surface* surface) { + int32_t ret = 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); - ret = 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); - - if (ret) - printf("drmModeSetCrtc failed ret=%d\n", ret); + if (ret) { + printf("drmModeSetCrtc failed ret=%d\n", ret); + } } static void drm_blank(minui_backend* backend __unused, bool blank) { - if (blank) - drm_disable_crtc(drm_fd, main_monitor_crtc); - else - drm_enable_crtc(drm_fd, main_monitor_crtc, - drm_surfaces[current_buffer]); + if (blank) { + drm_disable_crtc(drm_fd, main_monitor_crtc); + } else { + drm_enable_crtc(drm_fd, main_monitor_crtc, drm_surfaces[current_buffer]); + } } static void drm_destroy_surface(struct drm_surface *surface) { - struct drm_gem_close gem_close; - int ret; - - if(!surface) - return; + if (!surface) return; - if (surface->base.data) - munmap(surface->base.data, - surface->base.row_bytes * surface->base.height); + if (surface->base.data) { + munmap(surface->base.data, surface->base.row_bytes * surface->base.height); + } - if (surface->fb_id) { - ret = drmModeRmFB(drm_fd, surface->fb_id); - if (ret) - printf("drmModeRmFB failed ret=%d\n", ret); + if (surface->fb_id) { + int ret = drmModeRmFB(drm_fd, surface->fb_id); + if (ret) { + printf("drmModeRmFB failed ret=%d\n", ret); } + } - if (surface->handle) { - memset(&gem_close, 0, sizeof(gem_close)); - gem_close.handle = surface->handle; + if (surface->handle) { + drm_gem_close gem_close = {}; + gem_close.handle = surface->handle; - ret = drmIoctl(drm_fd, DRM_IOCTL_GEM_CLOSE, &gem_close); - if (ret) - printf("DRM_IOCTL_GEM_CLOSE failed ret=%d\n", ret); + int ret = drmIoctl(drm_fd, DRM_IOCTL_GEM_CLOSE, &gem_close); + if (ret) { + printf("DRM_IOCTL_GEM_CLOSE failed ret=%d\n", ret); } + } - free(surface); + free(surface); } static int drm_format_to_bpp(uint32_t format) { - switch(format) { - case DRM_FORMAT_ABGR8888: - case DRM_FORMAT_BGRA8888: - case DRM_FORMAT_RGBX8888: - case DRM_FORMAT_BGRX8888: - case DRM_FORMAT_XBGR8888: - case DRM_FORMAT_XRGB8888: - return 32; - case DRM_FORMAT_RGB565: - return 16; - default: - printf("Unknown format %d\n", format); - return 32; - } + switch (format) { + case DRM_FORMAT_ABGR8888: + case DRM_FORMAT_BGRA8888: + case DRM_FORMAT_RGBX8888: + case DRM_FORMAT_BGRX8888: + case DRM_FORMAT_XBGR8888: + case DRM_FORMAT_XRGB8888: + return 32; + case DRM_FORMAT_RGB565: + return 16; + default: + printf("Unknown format %d\n", format); + return 32; + } } static drm_surface *drm_create_surface(int width, int height) { - struct drm_surface *surface; - struct drm_mode_create_dumb create_dumb; - uint32_t format; - int ret; - - surface = (struct drm_surface*)calloc(1, sizeof(*surface)); - if (!surface) { - printf("Can't allocate memory\n"); - return NULL; - } + drm_surface* surface = static_cast<drm_surface*>(calloc(1, sizeof(*surface))); + if (!surface) { + printf("Can't allocate memory\n"); + return nullptr; + } + uint32_t format; #if defined(RECOVERY_ABGR) - format = DRM_FORMAT_RGBA8888; + format = DRM_FORMAT_RGBA8888; #elif defined(RECOVERY_BGRA) - format = DRM_FORMAT_ARGB8888; + format = DRM_FORMAT_ARGB8888; #elif defined(RECOVERY_RGBX) - format = DRM_FORMAT_XBGR8888; + format = DRM_FORMAT_XBGR8888; #else - format = DRM_FORMAT_RGB565; + format = DRM_FORMAT_RGB565; #endif - memset(&create_dumb, 0, sizeof(create_dumb)); - create_dumb.height = height; - create_dumb.width = width; - create_dumb.bpp = drm_format_to_bpp(format); - create_dumb.flags = 0; - - ret = drmIoctl(drm_fd, DRM_IOCTL_MODE_CREATE_DUMB, &create_dumb); - if (ret) { - printf("DRM_IOCTL_MODE_CREATE_DUMB failed ret=%d\n",ret); - drm_destroy_surface(surface); - return NULL; - } - surface->handle = create_dumb.handle; - - uint32_t handles[4], pitches[4], offsets[4]; - - handles[0] = surface->handle; - pitches[0] = create_dumb.pitch; - offsets[0] = 0; - - ret = drmModeAddFB2(drm_fd, width, height, - format, handles, pitches, offsets, - &(surface->fb_id), 0); - if (ret) { - printf("drmModeAddFB2 failed ret=%d\n", ret); - drm_destroy_surface(surface); - return NULL; - } - - struct drm_mode_map_dumb map_dumb; - memset(&map_dumb, 0, sizeof(map_dumb)); - map_dumb.handle = create_dumb.handle; - ret = drmIoctl(drm_fd, DRM_IOCTL_MODE_MAP_DUMB, &map_dumb); - if (ret) { - printf("DRM_IOCTL_MODE_MAP_DUMB failed ret=%d\n",ret); - drm_destroy_surface(surface); - return NULL;; - } - - surface->base.height = height; - surface->base.width = width; - surface->base.row_bytes = create_dumb.pitch; - surface->base.pixel_bytes = create_dumb.bpp / 8; - surface->base.data = (unsigned char*) - mmap(NULL, - surface->base.height * surface->base.row_bytes, - PROT_READ | PROT_WRITE, MAP_SHARED, - drm_fd, map_dumb.offset); - if (surface->base.data == MAP_FAILED) { - perror("mmap() failed"); - drm_destroy_surface(surface); - return NULL; - } - - return surface; + drm_mode_create_dumb create_dumb = {}; + create_dumb.height = height; + create_dumb.width = width; + create_dumb.bpp = drm_format_to_bpp(format); + create_dumb.flags = 0; + + int ret = drmIoctl(drm_fd, DRM_IOCTL_MODE_CREATE_DUMB, &create_dumb); + if (ret) { + printf("DRM_IOCTL_MODE_CREATE_DUMB failed ret=%d\n", ret); + drm_destroy_surface(surface); + return nullptr; + } + surface->handle = create_dumb.handle; + + uint32_t handles[4], pitches[4], offsets[4]; + + handles[0] = surface->handle; + pitches[0] = create_dumb.pitch; + offsets[0] = 0; + + ret = + drmModeAddFB2(drm_fd, width, height, format, handles, pitches, offsets, &(surface->fb_id), 0); + if (ret) { + printf("drmModeAddFB2 failed ret=%d\n", ret); + drm_destroy_surface(surface); + return nullptr; + } + + drm_mode_map_dumb map_dumb = {}; + map_dumb.handle = create_dumb.handle; + ret = drmIoctl(drm_fd, DRM_IOCTL_MODE_MAP_DUMB, &map_dumb); + if (ret) { + printf("DRM_IOCTL_MODE_MAP_DUMB failed ret=%d\n", ret); + drm_destroy_surface(surface); + return nullptr; + } + + surface->base.height = height; + surface->base.width = width; + surface->base.row_bytes = create_dumb.pitch; + surface->base.pixel_bytes = create_dumb.bpp / 8; + surface->base.data = + static_cast<unsigned char*>(mmap(nullptr, surface->base.height * surface->base.row_bytes, + PROT_READ | PROT_WRITE, MAP_SHARED, drm_fd, map_dumb.offset)); + if (surface->base.data == MAP_FAILED) { + perror("mmap() failed"); + drm_destroy_surface(surface); + return nullptr; + } + + return surface; } -static drmModeCrtc *find_crtc_for_connector(int fd, - drmModeRes *resources, - drmModeConnector *connector) { - int i, j; - drmModeEncoder *encoder; - int32_t crtc; - - /* - * Find the encoder. If we already have one, just use it. - */ - if (connector->encoder_id) - encoder = drmModeGetEncoder(fd, connector->encoder_id); - else - encoder = NULL; - - if (encoder && encoder->crtc_id) { - crtc = encoder->crtc_id; +static drmModeCrtc* find_crtc_for_connector(int fd, drmModeRes* resources, + drmModeConnector* connector) { + // Find the encoder. If we already have one, just use it. + drmModeEncoder* encoder; + if (connector->encoder_id) { + encoder = drmModeGetEncoder(fd, connector->encoder_id); + } else { + encoder = nullptr; + } + + int32_t crtc; + if (encoder && encoder->crtc_id) { + crtc = encoder->crtc_id; + drmModeFreeEncoder(encoder); + return drmModeGetCrtc(fd, crtc); + } + + // Didn't find anything, try to find a crtc and encoder combo. + crtc = -1; + for (int i = 0; i < connector->count_encoders; i++) { + encoder = drmModeGetEncoder(fd, connector->encoders[i]); + + if (encoder) { + for (int j = 0; j < resources->count_crtcs; j++) { + if (!(encoder->possible_crtcs & (1 << j))) continue; + crtc = resources->crtcs[j]; + break; + } + if (crtc >= 0) { drmModeFreeEncoder(encoder); return drmModeGetCrtc(fd, crtc); + } } + } - /* - * Didn't find anything, try to find a crtc and encoder combo. - */ - crtc = -1; - for (i = 0; i < connector->count_encoders; i++) { - encoder = drmModeGetEncoder(fd, connector->encoders[i]); - - if (encoder) { - for (j = 0; j < resources->count_crtcs; j++) { - if (!(encoder->possible_crtcs & (1 << j))) - continue; - crtc = resources->crtcs[j]; - break; - } - if (crtc >= 0) { - drmModeFreeEncoder(encoder); - return drmModeGetCrtc(fd, crtc); - } - } - } - - return NULL; + return nullptr; } -static drmModeConnector *find_used_connector_by_type(int fd, - drmModeRes *resources, - unsigned type) { - int i; - for (i = 0; i < resources->count_connectors; i++) { - drmModeConnector *connector; - - connector = drmModeGetConnector(fd, resources->connectors[i]); - if (connector) { - if ((connector->connector_type == type) && - (connector->connection == DRM_MODE_CONNECTED) && - (connector->count_modes > 0)) - return connector; - - drmModeFreeConnector(connector); - } +static drmModeConnector* find_used_connector_by_type(int fd, drmModeRes* resources, unsigned type) { + 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; + } + drmModeFreeConnector(connector); } - return NULL; + } + return nullptr; } -static drmModeConnector *find_first_connected_connector(int fd, - drmModeRes *resources) { - int i; - for (i = 0; i < resources->count_connectors; i++) { - drmModeConnector *connector; +static drmModeConnector* find_first_connected_connector(int fd, drmModeRes* resources) { + for (int i = 0; i < resources->count_connectors; i++) { + drmModeConnector* connector; - connector = drmModeGetConnector(fd, resources->connectors[i]); - if (connector) { - if ((connector->count_modes > 0) && - (connector->connection == DRM_MODE_CONNECTED)) - return connector; + connector = drmModeGetConnector(fd, resources->connectors[i]); + if (connector) { + if ((connector->count_modes > 0) && (connector->connection == DRM_MODE_CONNECTED)) + return connector; - drmModeFreeConnector(connector); - } + drmModeFreeConnector(connector); } - return NULL; + } + return nullptr; } -static drmModeConnector *find_main_monitor(int fd, drmModeRes *resources, - uint32_t *mode_index) { - unsigned i = 0; - int modes; - /* Look for LVDS/eDP/DSI connectors. Those are the main screens. */ - unsigned kConnectorPriority[] = { - DRM_MODE_CONNECTOR_LVDS, - DRM_MODE_CONNECTOR_eDP, - DRM_MODE_CONNECTOR_DSI, - }; - - drmModeConnector *main_monitor_connector = NULL; - do { - main_monitor_connector = find_used_connector_by_type(fd, - resources, - kConnectorPriority[i]); - i++; - } while (!main_monitor_connector && i < ARRAY_SIZE(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 NULL; - - *mode_index = 0; - for (modes = 0; modes < main_monitor_connector->count_modes; modes++) { - if (main_monitor_connector->modes[modes].type & - DRM_MODE_TYPE_PREFERRED) { - *mode_index = modes; - break; - } +static drmModeConnector* find_main_monitor(int fd, drmModeRes* resources, uint32_t* mode_index) { + /* Look for LVDS/eDP/DSI connectors. Those are the main screens. */ + static constexpr unsigned kConnectorPriority[] = { + DRM_MODE_CONNECTOR_LVDS, + DRM_MODE_CONNECTOR_eDP, + 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 < ARRAY_SIZE(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; + + *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) { + *mode_index = modes; + break; } + } - return main_monitor_connector; + return main_monitor_connector; } -static void disable_non_main_crtcs(int fd, - drmModeRes *resources, - drmModeCrtc* main_crtc) { - int i; - drmModeCrtc* crtc; - - for (i = 0; i < resources->count_connectors; i++) { - drmModeConnector *connector; - - connector = drmModeGetConnector(fd, resources->connectors[i]); - crtc = find_crtc_for_connector(fd, resources, connector); - if (crtc->crtc_id != main_crtc->crtc_id) - drm_disable_crtc(fd, crtc); - drmModeFreeCrtc(crtc); +static void disable_non_main_crtcs(int fd, drmModeRes* resources, drmModeCrtc* main_crtc) { + for (int i = 0; i < resources->count_connectors; i++) { + drmModeConnector* connector = drmModeGetConnector(fd, resources->connectors[i]); + drmModeCrtc* crtc = find_crtc_for_connector(fd, resources, connector); + if (crtc->crtc_id != main_crtc->crtc_id) { + drm_disable_crtc(fd, crtc); } + drmModeFreeCrtc(crtc); + } } static GRSurface* drm_init(minui_backend* backend __unused) { - drmModeRes *res = NULL; - uint32_t selected_mode; - char *dev_name; - int width, height; - int ret, i; - - /* Consider DRM devices in order. */ - for (i = 0; i < DRM_MAX_MINOR; i++) { - uint64_t cap = 0; - - ret = asprintf(&dev_name, DRM_DEV_NAME, DRM_DIR_NAME, i); - if (ret < 0) - continue; - - drm_fd = open(dev_name, O_RDWR, 0); - free(dev_name); - if (drm_fd < 0) - continue; - - /* We need dumb buffers. */ - ret = drmGetCap(drm_fd, DRM_CAP_DUMB_BUFFER, &cap); - if (ret || cap == 0) { - close(drm_fd); - continue; - } - - res = drmModeGetResources(drm_fd); - if (!res) { - close(drm_fd); - continue; - } - - /* Use this device if it has at least one connected monitor. */ - if (res->count_crtcs > 0 && res->count_connectors > 0) - if (find_first_connected_connector(drm_fd, res)) - break; - - drmModeFreeResources(res); - close(drm_fd); - res = NULL; + drmModeRes* res = nullptr; + + /* Consider DRM devices in order. */ + for (int i = 0; i < DRM_MAX_MINOR; i++) { + char* dev_name; + int ret = asprintf(&dev_name, DRM_DEV_NAME, DRM_DIR_NAME, i); + if (ret < 0) continue; + + drm_fd = open(dev_name, O_RDWR, 0); + free(dev_name); + if (drm_fd < 0) continue; + + uint64_t cap = 0; + /* We need dumb buffers. */ + ret = drmGetCap(drm_fd, DRM_CAP_DUMB_BUFFER, &cap); + if (ret || cap == 0) { + close(drm_fd); + continue; } - if (drm_fd < 0 || res == NULL) { - perror("cannot find/open a drm device"); - return NULL; + res = drmModeGetResources(drm_fd); + if (!res) { + close(drm_fd); + continue; } - main_monitor_connector = find_main_monitor(drm_fd, - res, &selected_mode); - - if (!main_monitor_connector) { - printf("main_monitor_connector not found\n"); - drmModeFreeResources(res); - close(drm_fd); - return NULL; + /* Use this device if it has at least one connected monitor. */ + if (res->count_crtcs > 0 && res->count_connectors > 0) { + if (find_first_connected_connector(drm_fd, res)) break; } - main_monitor_crtc = find_crtc_for_connector(drm_fd, res, - main_monitor_connector); + drmModeFreeResources(res); + close(drm_fd); + res = nullptr; + } - if (!main_monitor_crtc) { - printf("main_monitor_crtc not found\n"); - drmModeFreeResources(res); - close(drm_fd); - return NULL; - } + if (drm_fd < 0 || res == nullptr) { + perror("cannot find/open a drm device"); + return nullptr; + } - disable_non_main_crtcs(drm_fd, - res, main_monitor_crtc); + uint32_t selected_mode; + main_monitor_connector = find_main_monitor(drm_fd, res, &selected_mode); - main_monitor_crtc->mode = main_monitor_connector->modes[selected_mode]; + if (!main_monitor_connector) { + printf("main_monitor_connector not found\n"); + drmModeFreeResources(res); + close(drm_fd); + return nullptr; + } - width = main_monitor_crtc->mode.hdisplay; - height = main_monitor_crtc->mode.vdisplay; + main_monitor_crtc = find_crtc_for_connector(drm_fd, res, main_monitor_connector); + if (!main_monitor_crtc) { + printf("main_monitor_crtc not found\n"); drmModeFreeResources(res); + close(drm_fd); + return nullptr; + } - drm_surfaces[0] = drm_create_surface(width, height); - drm_surfaces[1] = drm_create_surface(width, height); - if (!drm_surfaces[0] || !drm_surfaces[1]) { - drm_destroy_surface(drm_surfaces[0]); - drm_destroy_surface(drm_surfaces[1]); - drmModeFreeResources(res); - close(drm_fd); - return NULL; - } + disable_non_main_crtcs(drm_fd, res, main_monitor_crtc); + + main_monitor_crtc->mode = main_monitor_connector->modes[selected_mode]; - current_buffer = 0; + int width = main_monitor_crtc->mode.hdisplay; + int height = main_monitor_crtc->mode.vdisplay; - drm_enable_crtc(drm_fd, main_monitor_crtc, drm_surfaces[1]); + drmModeFreeResources(res); - return &(drm_surfaces[0]->base); + drm_surfaces[0] = drm_create_surface(width, height); + drm_surfaces[1] = drm_create_surface(width, height); + if (!drm_surfaces[0] || !drm_surfaces[1]) { + drm_destroy_surface(drm_surfaces[0]); + drm_destroy_surface(drm_surfaces[1]); + drmModeFreeResources(res); + close(drm_fd); + return nullptr; + } + + current_buffer = 0; + + drm_enable_crtc(drm_fd, main_monitor_crtc, drm_surfaces[1]); + + return &(drm_surfaces[0]->base); } static GRSurface* drm_flip(minui_backend* backend __unused) { - int ret; - - ret = drmModePageFlip(drm_fd, main_monitor_crtc->crtc_id, - drm_surfaces[current_buffer]->fb_id, 0, NULL); - if (ret < 0) { - printf("drmModePageFlip failed ret=%d\n", ret); - return NULL; - } - current_buffer = 1 - current_buffer; - return &(drm_surfaces[current_buffer]->base); + int ret = drmModePageFlip(drm_fd, main_monitor_crtc->crtc_id, drm_surfaces[current_buffer]->fb_id, + 0, nullptr); + if (ret < 0) { + printf("drmModePageFlip failed ret=%d\n", ret); + return nullptr; + } + current_buffer = 1 - current_buffer; + return &(drm_surfaces[current_buffer]->base); } static void drm_exit(minui_backend* backend __unused) { - drm_disable_crtc(drm_fd, main_monitor_crtc); - drm_destroy_surface(drm_surfaces[0]); - drm_destroy_surface(drm_surfaces[1]); - drmModeFreeCrtc(main_monitor_crtc); - drmModeFreeConnector(main_monitor_connector); - close(drm_fd); - drm_fd = -1; + drm_disable_crtc(drm_fd, main_monitor_crtc); + drm_destroy_surface(drm_surfaces[0]); + drm_destroy_surface(drm_surfaces[1]); + drmModeFreeCrtc(main_monitor_crtc); + drmModeFreeConnector(main_monitor_connector); + close(drm_fd); + drm_fd = -1; } static minui_backend drm_backend = { - .init = drm_init, - .flip = drm_flip, - .blank = drm_blank, - .exit = drm_exit, + .init = drm_init, + .flip = drm_flip, + .blank = drm_blank, + .exit = drm_exit, }; minui_backend* open_drm() { - return &drm_backend; + return &drm_backend; } diff --git a/minui/graphics_fbdev.cpp b/minui/graphics_fbdev.cpp index 2d70249ed..dd4c9d465 100644 --- a/minui/graphics_fbdev.cpp +++ b/minui/graphics_fbdev.cpp @@ -14,20 +14,15 @@ * limitations under the License. */ -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - #include <fcntl.h> +#include <linux/fb.h> #include <stdio.h> - -#include <sys/cdefs.h> +#include <stdlib.h> +#include <string.h> #include <sys/ioctl.h> #include <sys/mman.h> #include <sys/types.h> - -#include <linux/fb.h> -#include <linux/kd.h> +#include <unistd.h> #include "minui/minui.h" #include "graphics.h" @@ -39,162 +34,157 @@ static void fbdev_exit(minui_backend*); static GRSurface gr_framebuffer[2]; static bool double_buffered; -static GRSurface* gr_draw = NULL; +static GRSurface* gr_draw = nullptr; static int displayed_buffer; static fb_var_screeninfo vi; static int fb_fd = -1; static minui_backend my_backend = { - .init = fbdev_init, - .flip = fbdev_flip, - .blank = fbdev_blank, - .exit = fbdev_exit, + .init = fbdev_init, + .flip = fbdev_flip, + .blank = fbdev_blank, + .exit = fbdev_exit, }; minui_backend* open_fbdev() { - return &my_backend; + return &my_backend; } -static void fbdev_blank(minui_backend* backend __unused, bool blank) -{ - int ret; - - ret = ioctl(fb_fd, FBIOBLANK, blank ? FB_BLANK_POWERDOWN : FB_BLANK_UNBLANK); - if (ret < 0) - perror("ioctl(): blank"); +static void fbdev_blank(minui_backend* backend __unused, bool blank) { + int ret = ioctl(fb_fd, FBIOBLANK, blank ? FB_BLANK_POWERDOWN : FB_BLANK_UNBLANK); + if (ret < 0) { + perror("ioctl(): blank"); + } } -static void set_displayed_framebuffer(unsigned n) -{ - if (n > 1 || !double_buffered) return; +static void set_displayed_framebuffer(unsigned n) { + if (n > 1 || !double_buffered) return; - vi.yres_virtual = gr_framebuffer[0].height * 2; - vi.yoffset = n * gr_framebuffer[0].height; - vi.bits_per_pixel = gr_framebuffer[0].pixel_bytes * 8; - if (ioctl(fb_fd, FBIOPUT_VSCREENINFO, &vi) < 0) { - perror("active fb swap failed"); - } - displayed_buffer = n; + vi.yres_virtual = gr_framebuffer[0].height * 2; + vi.yoffset = n * gr_framebuffer[0].height; + vi.bits_per_pixel = gr_framebuffer[0].pixel_bytes * 8; + if (ioctl(fb_fd, FBIOPUT_VSCREENINFO, &vi) < 0) { + perror("active fb swap failed"); + } + displayed_buffer = n; } static GRSurface* fbdev_init(minui_backend* backend) { - int fd = open("/dev/graphics/fb0", O_RDWR); - if (fd == -1) { - perror("cannot open fb0"); - return NULL; + int fd = open("/dev/graphics/fb0", O_RDWR); + if (fd == -1) { + perror("cannot open fb0"); + return nullptr; + } + + fb_fix_screeninfo fi; + if (ioctl(fd, FBIOGET_FSCREENINFO, &fi) < 0) { + perror("failed to get fb0 info"); + close(fd); + return nullptr; + } + + if (ioctl(fd, FBIOGET_VSCREENINFO, &vi) < 0) { + perror("failed to get fb0 info"); + close(fd); + return nullptr; + } + + // We print this out for informational purposes only, but + // throughout we assume that the framebuffer device uses an RGBX + // pixel format. This is the case for every development device I + // have access to. For some of those devices (eg, hammerhead aka + // Nexus 5), FBIOGET_VSCREENINFO *reports* that it wants a + // different format (XBGR) but actually produces the correct + // results on the display when you write RGBX. + // + // If you have a device that actually *needs* another pixel format + // (ie, BGRX, or 565), patches welcome... + + printf( + "fb0 reports (possibly inaccurate):\n" + " vi.bits_per_pixel = %d\n" + " vi.red.offset = %3d .length = %3d\n" + " vi.green.offset = %3d .length = %3d\n" + " vi.blue.offset = %3d .length = %3d\n", + vi.bits_per_pixel, vi.red.offset, vi.red.length, vi.green.offset, vi.green.length, + vi.blue.offset, vi.blue.length); + + void* bits = mmap(0, fi.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (bits == MAP_FAILED) { + perror("failed to mmap framebuffer"); + close(fd); + return nullptr; + } + + memset(bits, 0, fi.smem_len); + + gr_framebuffer[0].width = vi.xres; + gr_framebuffer[0].height = vi.yres; + gr_framebuffer[0].row_bytes = fi.line_length; + gr_framebuffer[0].pixel_bytes = vi.bits_per_pixel / 8; + gr_framebuffer[0].data = static_cast<uint8_t*>(bits); + memset(gr_framebuffer[0].data, 0, gr_framebuffer[0].height * gr_framebuffer[0].row_bytes); + + /* check if we can use double buffering */ + if (vi.yres * fi.line_length * 2 <= fi.smem_len) { + double_buffered = true; + + memcpy(gr_framebuffer + 1, gr_framebuffer, sizeof(GRSurface)); + gr_framebuffer[1].data = + gr_framebuffer[0].data + gr_framebuffer[0].height * gr_framebuffer[0].row_bytes; + + gr_draw = gr_framebuffer + 1; + + } else { + double_buffered = false; + + // Without double-buffering, we allocate RAM for a buffer to + // draw in, and then "flipping" the buffer consists of a + // memcpy from the buffer we allocated to the framebuffer. + + gr_draw = static_cast<GRSurface*>(malloc(sizeof(GRSurface))); + memcpy(gr_draw, gr_framebuffer, sizeof(GRSurface)); + gr_draw->data = static_cast<unsigned char*>(malloc(gr_draw->height * gr_draw->row_bytes)); + if (!gr_draw->data) { + perror("failed to allocate in-memory surface"); + return nullptr; } + } - fb_fix_screeninfo fi; - if (ioctl(fd, FBIOGET_FSCREENINFO, &fi) < 0) { - perror("failed to get fb0 info"); - close(fd); - return NULL; - } + memset(gr_draw->data, 0, gr_draw->height * gr_draw->row_bytes); + fb_fd = fd; + set_displayed_framebuffer(0); - if (ioctl(fd, FBIOGET_VSCREENINFO, &vi) < 0) { - perror("failed to get fb0 info"); - close(fd); - return NULL; - } + printf("framebuffer: %d (%d x %d)\n", fb_fd, gr_draw->width, gr_draw->height); - // We print this out for informational purposes only, but - // throughout we assume that the framebuffer device uses an RGBX - // pixel format. This is the case for every development device I - // have access to. For some of those devices (eg, hammerhead aka - // Nexus 5), FBIOGET_VSCREENINFO *reports* that it wants a - // different format (XBGR) but actually produces the correct - // results on the display when you write RGBX. - // - // If you have a device that actually *needs* another pixel format - // (ie, BGRX, or 565), patches welcome... - - printf("fb0 reports (possibly inaccurate):\n" - " vi.bits_per_pixel = %d\n" - " vi.red.offset = %3d .length = %3d\n" - " vi.green.offset = %3d .length = %3d\n" - " vi.blue.offset = %3d .length = %3d\n", - vi.bits_per_pixel, - vi.red.offset, vi.red.length, - vi.green.offset, vi.green.length, - vi.blue.offset, vi.blue.length); - - void* bits = mmap(0, fi.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); - if (bits == MAP_FAILED) { - perror("failed to mmap framebuffer"); - close(fd); - return NULL; - } + fbdev_blank(backend, true); + fbdev_blank(backend, false); - memset(bits, 0, fi.smem_len); - - gr_framebuffer[0].width = vi.xres; - gr_framebuffer[0].height = vi.yres; - gr_framebuffer[0].row_bytes = fi.line_length; - gr_framebuffer[0].pixel_bytes = vi.bits_per_pixel / 8; - gr_framebuffer[0].data = static_cast<uint8_t*>(bits); - memset(gr_framebuffer[0].data, 0, gr_framebuffer[0].height * gr_framebuffer[0].row_bytes); - - /* check if we can use double buffering */ - if (vi.yres * fi.line_length * 2 <= fi.smem_len) { - double_buffered = true; - - memcpy(gr_framebuffer+1, gr_framebuffer, sizeof(GRSurface)); - gr_framebuffer[1].data = gr_framebuffer[0].data + - gr_framebuffer[0].height * gr_framebuffer[0].row_bytes; - - gr_draw = gr_framebuffer+1; - - } else { - double_buffered = false; - - // Without double-buffering, we allocate RAM for a buffer to - // draw in, and then "flipping" the buffer consists of a - // memcpy from the buffer we allocated to the framebuffer. - - gr_draw = (GRSurface*) malloc(sizeof(GRSurface)); - memcpy(gr_draw, gr_framebuffer, sizeof(GRSurface)); - gr_draw->data = (unsigned char*) malloc(gr_draw->height * gr_draw->row_bytes); - if (!gr_draw->data) { - perror("failed to allocate in-memory surface"); - return NULL; - } - } - - memset(gr_draw->data, 0, gr_draw->height * gr_draw->row_bytes); - fb_fd = fd; - set_displayed_framebuffer(0); - - printf("framebuffer: %d (%d x %d)\n", fb_fd, gr_draw->width, gr_draw->height); - - fbdev_blank(backend, true); - fbdev_blank(backend, false); - - return gr_draw; + return gr_draw; } static GRSurface* fbdev_flip(minui_backend* backend __unused) { - if (double_buffered) { - // Change gr_draw to point to the buffer currently displayed, - // then flip the driver so we're displaying the other buffer - // instead. - gr_draw = gr_framebuffer + displayed_buffer; - set_displayed_framebuffer(1-displayed_buffer); - } else { - // Copy from the in-memory surface to the framebuffer. - memcpy(gr_framebuffer[0].data, gr_draw->data, - gr_draw->height * gr_draw->row_bytes); - } - return gr_draw; + if (double_buffered) { + // Change gr_draw to point to the buffer currently displayed, + // then flip the driver so we're displaying the other buffer + // instead. + gr_draw = gr_framebuffer + displayed_buffer; + set_displayed_framebuffer(1 - displayed_buffer); + } else { + // Copy from the in-memory surface to the framebuffer. + memcpy(gr_framebuffer[0].data, gr_draw->data, gr_draw->height * gr_draw->row_bytes); + } + return gr_draw; } static void fbdev_exit(minui_backend* backend __unused) { - close(fb_fd); - fb_fd = -1; - - if (!double_buffered && gr_draw) { - free(gr_draw->data); - free(gr_draw); - } - gr_draw = NULL; + close(fb_fd); + fb_fd = -1; + + if (!double_buffered && gr_draw) { + free(gr_draw->data); + free(gr_draw); + } + gr_draw = nullptr; } |