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, 681 insertions, 583 deletions
diff --git a/minui/graphics_adf.cpp b/minui/graphics_adf.cpp index 9ab0b06bf..9e262b044 100644 --- a/minui/graphics_adf.cpp +++ b/minui/graphics_adf.cpp @@ -16,224 +16,258 @@ #include <errno.h> #include <fcntl.h> +#include <stdbool.h> #include <stdio.h> #include <stdlib.h> -#include <sys/mman.h> +#include <string.h> #include <unistd.h> +#include <sys/cdefs.h> +#include <sys/mman.h> + #include <adf/adf.h> #include <sync/sync.h> #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) { - *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; - } +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)); - return 0; + 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; + } + + return 0; } -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; - } +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; + } 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 err = adf_find_simple_post_configuration(dev, &pdata->format, 1, &intf_id, &pdata->eng_id); - if (err < 0) return err; +static int adf_device_init(adf_pdata *pdata, adf_device *dev) +{ + adf_id_t intf_id; + int intf_fd; + int err; - err = adf_device_attach(dev, pdata->eng_id, intf_id); - if (err < 0 && err != -EALREADY) return err; + err = adf_find_simple_post_configuration(dev, &pdata->format, 1, &intf_id, + &pdata->eng_id); + if (err < 0) + return err; - pdata->intf_fd = adf_interface_open(dev, intf_id, O_RDWR); - if (pdata->intf_fd < 0) return pdata->intf_fd; + err = adf_device_attach(dev, pdata->eng_id, intf_id); + if (err < 0 && err != -EALREADY) + return err; - err = adf_interface_init(pdata); - if (err < 0) { - close(pdata->intf_fd); - pdata->intf_fd = -1; - } + 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; + } - return err; + return err; } -static GRSurface* adf_init(minui_backend* backend) { - adf_pdata* pdata = reinterpret_cast<adf_pdata*>(backend); +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; #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 - 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; - - 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; + 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; } - 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); + 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); + } } - } - free(dev_ids); + free(dev_ids); - if (pdata->intf_fd < 0) return nullptr; + if (pdata->intf_fd < 0) + return NULL; - GRSurface* ret = adf_flip(backend); + ret = adf_flip(backend); - adf_blank(backend, true); - adf_blank(backend, false); + adf_blank(backend, true); + adf_blank(backend, false); - return ret; + return ret; } -static void adf_sync(adf_surface_pdata* surf) { - static constexpr unsigned int warningTimeout = 3000; +static void adf_sync(adf_surface_pdata *surf) +{ + unsigned int warningTimeout = 3000; - if (surf == nullptr) return; + if (surf == NULL) + return; - 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->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; - } + close(surf->fence_fd); + surf->fence_fd = -1; + } } -static GRSurface* adf_flip(minui_backend* backend) { - adf_pdata* pdata = reinterpret_cast<adf_pdata*>(backend); - adf_surface_pdata* surf = &pdata->surfaces[pdata->current_surface]; +static GRSurface* adf_flip(minui_backend *backend) +{ + adf_pdata *pdata = (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 = reinterpret_cast<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 = (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 = 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); +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); } -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; +minui_backend *open_adf() +{ + adf_pdata* pdata = static_cast<adf_pdata*>(calloc(1, sizeof(*pdata))); + if (!pdata) { + perror("allocating adf backend failed"); + return NULL; + } - return &pdata->base; + pdata->base.init = adf_init; + pdata->base.flip = adf_flip; + pdata->base.blank = adf_blank; + pdata->base.exit = adf_exit; + return &pdata->base; } diff --git a/minui/graphics_drm.cpp b/minui/graphics_drm.cpp index 2eeff5800..199f4d83c 100644 --- a/minui/graphics_drm.cpp +++ b/minui/graphics_drm.cpp @@ -14,14 +14,16 @@ * 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> @@ -31,9 +33,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]; @@ -45,377 +47,429 @@ 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 - nullptr, // connectors - 0, // connector_count - nullptr); // mode - } + if (crtc) { + drmModeSetCrtc(drm_fd, crtc->crtc_id, + 0, // fb_id + 0, 0, // x,y + NULL, // connectors + 0, // connector_count + NULL); // mode + } } -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); +static void drm_enable_crtc(int drm_fd, drmModeCrtc *crtc, + struct drm_surface *surface) { + int32_t ret; - if (ret) { - printf("drmModeSetCrtc failed ret=%d\n", ret); - } + 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); } 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) { - if (!surface) return; + struct drm_gem_close gem_close; + int ret; - if (surface->base.data) { - munmap(surface->base.data, surface->base.row_bytes * surface->base.height); - } + if(!surface) + return; - if (surface->fb_id) { - int ret = drmModeRmFB(drm_fd, surface->fb_id); - if (ret) { - printf("drmModeRmFB failed ret=%d\n", ret); + 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->handle) { - drm_gem_close gem_close = {}; - gem_close.handle = surface->handle; + if (surface->handle) { + memset(&gem_close, 0, sizeof(gem_close)); + gem_close.handle = surface->handle; - int ret = drmIoctl(drm_fd, DRM_IOCTL_GEM_CLOSE, &gem_close); - if (ret) { - printf("DRM_IOCTL_GEM_CLOSE failed ret=%d\n", ret); + 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) { - drm_surface* surface = static_cast<drm_surface*>(calloc(1, sizeof(*surface))); - if (!surface) { - printf("Can't allocate memory\n"); - return nullptr; - } + 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; + } - 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 - 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; + 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; } -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) { +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; drmModeFreeEncoder(encoder); return drmModeGetCrtc(fd, crtc); - } } - } - return nullptr; + /* + * 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; } -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); +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); + } } - } - return nullptr; + return NULL; } -static drmModeConnector* find_first_connected_connector(int fd, drmModeRes* resources) { - for (int i = 0; i < resources->count_connectors; i++) { - drmModeConnector* connector; +static drmModeConnector *find_first_connected_connector(int fd, + drmModeRes *resources) { + int i; + for (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 nullptr; + return NULL; } -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; +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; + } } - } - return main_monitor_connector; + return main_monitor_connector; } -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); +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); } - drmModeFreeCrtc(crtc); - } } static GRSurface* drm_init(minui_backend* backend __unused) { - 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; + 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; } - res = drmModeGetResources(drm_fd); - if (!res) { - close(drm_fd); - continue; + if (drm_fd < 0 || res == NULL) { + perror("cannot find/open a drm device"); + 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_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; } - drmModeFreeResources(res); - close(drm_fd); - res = nullptr; - } + main_monitor_crtc = find_crtc_for_connector(drm_fd, res, + main_monitor_connector); - if (drm_fd < 0 || res == nullptr) { - perror("cannot find/open a drm device"); - return nullptr; - } + if (!main_monitor_crtc) { + printf("main_monitor_crtc not found\n"); + drmModeFreeResources(res); + close(drm_fd); + return NULL; + } - uint32_t selected_mode; - main_monitor_connector = find_main_monitor(drm_fd, res, &selected_mode); + disable_non_main_crtcs(drm_fd, + res, main_monitor_crtc); - if (!main_monitor_connector) { - printf("main_monitor_connector not found\n"); - drmModeFreeResources(res); - close(drm_fd); - return nullptr; - } + main_monitor_crtc->mode = main_monitor_connector->modes[selected_mode]; - main_monitor_crtc = find_crtc_for_connector(drm_fd, res, main_monitor_connector); + width = main_monitor_crtc->mode.hdisplay; + height = main_monitor_crtc->mode.vdisplay; - if (!main_monitor_crtc) { - printf("main_monitor_crtc not found\n"); drmModeFreeResources(res); - close(drm_fd); - return nullptr; - } - - disable_non_main_crtcs(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; - drmModeFreeResources(res); - - 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; - } + 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; + } - current_buffer = 0; + current_buffer = 0; - drm_enable_crtc(drm_fd, main_monitor_crtc, drm_surfaces[1]); + drm_enable_crtc(drm_fd, main_monitor_crtc, drm_surfaces[1]); - return &(drm_surfaces[0]->base); + return &(drm_surfaces[0]->base); } static GRSurface* drm_flip(minui_backend* backend __unused) { - 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); + 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); } 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 dd4c9d465..2d70249ed 100644 --- a/minui/graphics_fbdev.cpp +++ b/minui/graphics_fbdev.cpp @@ -14,15 +14,20 @@ * limitations under the License. */ -#include <fcntl.h> -#include <linux/fb.h> -#include <stdio.h> #include <stdlib.h> #include <string.h> +#include <unistd.h> + +#include <fcntl.h> +#include <stdio.h> + +#include <sys/cdefs.h> #include <sys/ioctl.h> #include <sys/mman.h> #include <sys/types.h> -#include <unistd.h> + +#include <linux/fb.h> +#include <linux/kd.h> #include "minui/minui.h" #include "graphics.h" @@ -34,157 +39,162 @@ static void fbdev_exit(minui_backend*); static GRSurface gr_framebuffer[2]; static bool double_buffered; -static GRSurface* gr_draw = nullptr; +static GRSurface* gr_draw = NULL; 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 = 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; + + 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 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; + int fd = open("/dev/graphics/fb0", O_RDWR); + if (fd == -1) { + perror("cannot open fb0"); + return NULL; } - } - memset(gr_draw->data, 0, gr_draw->height * gr_draw->row_bytes); - fb_fd = fd; - set_displayed_framebuffer(0); + fb_fix_screeninfo fi; + if (ioctl(fd, FBIOGET_FSCREENINFO, &fi) < 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); + if (ioctl(fd, FBIOGET_VSCREENINFO, &vi) < 0) { + perror("failed to get fb0 info"); + close(fd); + return NULL; + } - fbdev_blank(backend, true); - fbdev_blank(backend, false); + // 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; + } - return gr_draw; + 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; } 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 = nullptr; + close(fb_fd); + fb_fd = -1; + + if (!double_buffered && gr_draw) { + free(gr_draw->data); + free(gr_draw); + } + gr_draw = NULL; } |