#include #ifndef __APPLE__ #include #ifndef MW_OPENGL #define MW_OPENGL_NO_INCLUDE #endif #include #ifdef MW_OPENGL #ifdef USE_GDI typedef HGLRC(WINAPI* MWwglCreateContext)(HDC); typedef BOOL(WINAPI* MWwglMakeCurrent)(HDC, HGLRC); typedef PROC(WINAPI* MWwglGetProcAddress)(LPCSTR); typedef BOOL(WINAPI* MWwglDeleteContext)(HGLRC); typedef struct gdiopengl { HDC dc; HGLRC gl; void* lib; MWwglCreateContext wglCreateContext; MWwglMakeCurrent wglMakeCurrent; MWwglDeleteContext wglDeleteContext; MWwglGetProcAddress wglGetProcAddress; } gdiopengl_t; #endif #ifdef USE_X11 typedef XVisualInfo* (*MWglXChooseVisual)(Display* dpy, int screen, int* attribList); typedef GLXContext (*MWglXCreateContext)(Display* dpy, XVisualInfo* vis, GLXContext shareList, Bool direct); typedef void (*MWglXDestroyContext)(Display* dpy, GLXContext ctx); typedef Bool (*MWglXMakeCurrent)(Display* dpy, GLXDrawable drawable, GLXContext ctx); typedef void (*MWglXSwapBuffers)(Display* dpy, GLXDrawable drawable); typedef void* (*MWglXGetProcAddress)(const GLubyte* procname); typedef struct x11opengl { XVisualInfo* visual; GLXContext gl; void* lib; MWglXChooseVisual glXChooseVisual; MWglXCreateContext glXCreateContext; MWglXDestroyContext glXDestroyContext; MWglXMakeCurrent glXMakeCurrent; MWglXSwapBuffers glXSwapBuffers; MWglXGetProcAddress glXGetProcAddress; } x11opengl_t; #endif #ifdef USE_WAYLAND #include #include #include typedef void* EGLLabelKHR; typedef struct waylandopengl { EGLDisplay egl_display; EGLContext egl_context; EGLSurface egl_surface; EGLConfig egl_config; struct gbm_device* gbm; struct gbm_surface* gbm_surface; struct gbm_bo* previous_bo; int drm_fd; MwLLPixmap frontbuffer; MwU8* frontbuffer_data; void* gllib; void* gbmlib; EGLBoolean (*eglGetConfigs)(EGLDisplay display, EGLConfig* configs, EGLint config_size, EGLint* num_config); EGLBoolean (*eglInitialize)(EGLDisplay display, EGLint* major, EGLint* minor); EGLContext (*eglCreateContext)(EGLDisplay display, EGLConfig config, EGLContext share_context, EGLint const* attrib_list); EGLBoolean (*eglMakeCurrent)(EGLDisplay display, EGLSurface draw, EGLSurface read, EGLContext context); void* (*eglGetProcAddress)(char const* procname); EGLBoolean (*eglSwapBuffers)(EGLDisplay display, EGLSurface surface); EGLBoolean (*eglSwapInterval)(EGLDisplay display, EGLint interval); EGLint (*eglGetError)(void); EGLBoolean (*eglBindAPI)(EGLenum api); EGLDisplay (*eglGetDisplay)(NativeDisplayType native_display); EGLDisplay (*eglGetPlatformDisplay)(EGLenum platform, void* native_display, const EGLAttrib* attrib_list); EGLSurface (*eglCreatePlatformWindowSurface)(EGLDisplay display, EGLConfig config, void* native_window, EGLint const* attrib_list); EGLBoolean (*eglChooseConfig)(EGLDisplay display, EGLint const* attrib_list, EGLConfig* configs, EGLint config_size, EGLint* num_config); EGLBoolean (*eglGetConfigAttrib)(EGLDisplay dpy, EGLConfig config, EGLint attribute, EGLint* value); struct wl_egl_window* (*wl_egl_window_create)(struct wl_surface* surface, int width, int height); void (*wl_egl_window_resize)(struct wl_egl_window* egl_window, int width, int height, int dx, int dy); struct gbm_device* (*gbm_create_device)(int fd); struct gbm_surface* (*gbm_surface_create)(struct gbm_device* gbm, uint32_t width, uint32_t height, uint32_t format, uint32_t flags); struct gbm_bo* (*gbm_surface_lock_front_buffer)(struct gbm_surface* surface); union gbm_bo_handle (*gbm_bo_get_handle)(struct gbm_bo* bo); uint32_t (*gbm_bo_get_stride)(struct gbm_bo* bo); uint32_t (*gbm_bo_get_width)(struct gbm_bo* bo); uint32_t (*gbm_bo_get_height)(struct gbm_bo* bo); void (*gbm_surface_release_buffer)(struct gbm_surface* surface, struct gbm_bo* bo); void* (*gbm_bo_map)(struct gbm_bo* bo, uint32_t x, uint32_t y, uint32_t width, uint32_t height, uint32_t flags, uint32_t* stride, void** map_data); void (*gbm_bo_unmap)(struct gbm_bo* bo, void* map_data); } waylandopengl_t; #endif static int wcreate(MwWidget handle) { void* r = NULL; MwWidget w = handle; #ifdef USE_GDI if(handle->lowlevel->common.type == MwLLBackendGDI) { PIXELFORMATDESCRIPTOR pfd; int pf; gdiopengl_t* o = r = malloc(sizeof(*o)); memset(&pfd, 0, sizeof(pfd)); pfd.nSize = sizeof(pfd); pfd.nVersion = 1; pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; pfd.cDepthBits = 32; pfd.cColorBits = 32; o->dc = GetDC(handle->lowlevel->gdi.hWnd); pf = ChoosePixelFormat(o->dc, &pfd); SetPixelFormat(o->dc, pf, &pfd); o->lib = MwDynamicOpen("opengl32.dll"); if(!o->lib) { printf("Could not load OpenGL widget under WGL!\n"); return 1; } #define WGL_FUNC(x) \ o->x = MwDynamicSymbol(o->lib, #x); \ if(o->x == NULL) { \ printf("Could not load OpenGL widget under WGL! " #x " missing.\n"); \ return 1; \ } WGL_FUNC(wglCreateContext); WGL_FUNC(wglMakeCurrent); WGL_FUNC(wglDeleteContext); WGL_FUNC(wglGetProcAddress); o->gl = o->wglCreateContext(o->dc); } #endif #ifdef USE_X11 if(handle->lowlevel->common.type == MwLLBackendX11) { int attribs[5]; const char* glpath[] = {"libGL.so", "/usr/local/lib/libGL.so", "/usr/X11R7/lib/libGL.so", "/usr/pkg/lib/libGL.so"}; int glincr = 0; x11opengl_t* o = r = malloc(sizeof(*o)); attribs[0] = GLX_RGBA; attribs[1] = GLX_DOUBLEBUFFER; attribs[2] = GLX_DEPTH_SIZE; attribs[3] = 24; attribs[4] = None; while(glpath[glincr] != NULL && (o->lib = MwDynamicOpen(glpath[glincr++])) == NULL); o->glXChooseVisual = (MWglXChooseVisual)MwDynamicSymbol(o->lib, "glXChooseVisual"); o->glXCreateContext = (MWglXCreateContext)MwDynamicSymbol(o->lib, "glXCreateContext"); o->glXDestroyContext = (MWglXDestroyContext)MwDynamicSymbol(o->lib, "glXDestroyContext"); o->glXMakeCurrent = (MWglXMakeCurrent)MwDynamicSymbol(o->lib, "glXMakeCurrent"); o->glXSwapBuffers = (MWglXSwapBuffers)MwDynamicSymbol(o->lib, "glXSwapBuffers"); o->glXGetProcAddress = (MWglXGetProcAddress)MwDynamicSymbol(o->lib, "glXGetProcAddress"); /* XXX: fix this */ o->visual = o->glXChooseVisual(handle->lowlevel->x11.display, DefaultScreen(handle->lowlevel->x11.display), attribs); o->gl = o->glXCreateContext(handle->lowlevel->x11.display, o->visual, NULL, GL_TRUE); } #endif #ifdef USE_WAYLAND if(handle->lowlevel->common.type == MwLLBackendWayland) { EGLint numConfigs; EGLint numConfigsMatched; EGLint majorVersion; EGLint minorVersion; EGLContext context; EGLSurface surface; EGLint fbAttribs[] = {EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_DEPTH_SIZE, 24, EGL_NONE}; EGLint contextAttribs[] = {EGL_CONTEXT_CLIENT_VERSION, 1, EGL_CONTEXT_MAJOR_VERSION, 1, EGL_CONTEXT_MINOR_VERSION, 1, EGL_NONE}; EGLDisplay display; waylandopengl_t* o = r = malloc(sizeof(*o)); int gbm_format = 0; memset(o, 0, sizeof(waylandopengl_t)); o->gllib = MwDynamicOpen("libEGL.so"); if(!o->gllib) { printf("Could not load OpenGL widget under Wayland! libEGL.so missing.\n"); return 1; } o->gbmlib = MwDynamicOpen("libgbm.so"); if(!o->gbmlib) { printf("Could not load OpenGL widget under Wayland! libgbm.so missing.\n"); return 1; } #define EGL_FUNC(x) \ o->x = MwDynamicSymbol(o->gllib, #x); \ if(!o->x) { \ printf("Could not load OpenGL widget under Wayland! " #x " missing.\n"); \ return 1; \ }; #define GBM_FUNC(x) \ o->x = MwDynamicSymbol(o->gbmlib, #x); \ if(!o->x) { \ printf("Could not load OpenGL widget under Wayland! " #x " missing.\n"); \ return 1; \ }; EGL_FUNC(eglGetConfigs) EGL_FUNC(eglInitialize) EGL_FUNC(eglCreateContext) EGL_FUNC(eglMakeCurrent) EGL_FUNC(eglGetProcAddress) EGL_FUNC(eglSwapBuffers) EGL_FUNC(eglSwapInterval) EGL_FUNC(eglGetError) EGL_FUNC(eglBindAPI) EGL_FUNC(eglGetDisplay) EGL_FUNC(eglCreatePlatformWindowSurface) EGL_FUNC(eglChooseConfig) EGL_FUNC(eglGetPlatformDisplay) EGL_FUNC(eglGetConfigAttrib) GBM_FUNC(gbm_create_device) GBM_FUNC(gbm_surface_create) GBM_FUNC(gbm_surface_lock_front_buffer) GBM_FUNC(gbm_bo_get_handle) GBM_FUNC(gbm_bo_get_stride) GBM_FUNC(gbm_bo_get_width) GBM_FUNC(gbm_bo_get_height) GBM_FUNC(gbm_surface_release_buffer) GBM_FUNC(gbm_bo_map) GBM_FUNC(gbm_bo_unmap) #define eglGetConfigs o->eglGetConfigs #define eglInitialize o->eglInitialize #define eglCreateContext o->eglCreateContext #define eglMakeCurrent o->eglMakeCurrent #define eglGetProcAddress o->eglGetProcAddress #define eglSwapBuffers o->eglSwapBuffers #define eglSwapInterval o->eglSwapInterval #define eglGetError o->eglGetError #define wl_egl_window_create o->wl_egl_window_create #define eglBindAPI o->eglBindAPI #define eglGetDisplay o->eglGetDisplay #define wl_egl_window_resize o->wl_egl_window_resize #define eglCreatePlatformWindowSurface o->eglCreatePlatformWindowSurface #define eglChooseConfig o->eglChooseConfig #define eglGetPlatformDisplay o->eglGetPlatformDisplay o->drm_fd = open("/dev/dri/renderD128", O_RDWR); assert(o->drm_fd > 0); o->gbm = o->gbm_create_device(o->drm_fd); assert(o->gbm != NULL); display = eglGetPlatformDisplay(0x31D7, o->gbm, NULL); if(display == EGL_NO_DISPLAY) { printf("ERROR: eglGetDisplay, %0X\n", eglGetError()); return 1; } /* Initialize EGL */ if(!eglInitialize(display, &majorVersion, &minorVersion)) { printf("ERROR: eglInitialize, %0X\n", eglGetError()); return 1; } eglBindAPI(EGL_OPENGL_API); /* Get configs */ if((eglGetConfigs(display, NULL, 0, &numConfigs) != EGL_TRUE)) { printf("ERROR: eglGetConfigs, %0X\n", eglGetError()); return 1; } EGLConfig egl_configs[1024]; /* Choose config */ if((eglChooseConfig(display, fbAttribs, egl_configs, numConfigs, &numConfigsMatched) != EGL_TRUE)) { printf("ERROR: eglChooseConfig, %0X\n", eglGetError()); return 1; } for(int i = 0; i < numConfigsMatched; i++) { if(o->eglGetConfigAttrib(display, egl_configs[i], EGL_NATIVE_VISUAL_ID, &gbm_format) == EGL_FALSE) { printf("ERROR: eglGetConfigAttrib, %0X\n", eglGetError()); } else { if(gbm_format == GBM_FORMAT_ARGB8888) { o->egl_config = egl_configs[i]; } } } /* Create a GL context */ context = eglCreateContext(display, o->egl_config, EGL_NO_CONTEXT, contextAttribs); if(context == EGL_NO_CONTEXT) { printf("ERROR: eglCreateContext, %0X\n", eglGetError()); return 1; } o->gbm_surface = o->gbm_surface_create(o->gbm, handle->lowlevel->wayland.ww, handle->lowlevel->wayland.wh, GBM_FORMAT_ARGB8888, GBM_BO_USE_LINEAR | GBM_BO_USE_RENDERING); /* Create a surface */ surface = eglCreatePlatformWindowSurface(display, o->egl_config, o->gbm_surface, NULL); if(surface == EGL_NO_SURFACE) { printf("ERROR: eglCreatePlatformWindowSurface, %0X\n", eglGetError()); return 1; } if(!eglMakeCurrent(display, surface, surface, context)) { printf("ERROR: eglMakeCurrent (setup): %0X\n", eglGetError()); } o->frontbuffer_data = malloc(handle->lowlevel->wayland.ww * handle->lowlevel->wayland.wh * 4); memset(o->frontbuffer_data, 255, handle->lowlevel->wayland.ww * handle->lowlevel->wayland.wh * 4); o->frontbuffer = MwLoadRaw(handle, o->frontbuffer_data, handle->lowlevel->wayland.ww, handle->lowlevel->wayland.wh); MwVaApply(handle, MwNpixmap, o->frontbuffer, NULL); o->egl_display = display; o->egl_surface = surface; o->egl_context = context; } #endif handle->internal = r; handle->lowlevel->common.copy_buffer = 0; MwSetDefault(handle); while(w->parent != NULL) w = w->parent; w->berserk++; return 0; } static void destroy(MwWidget handle) { MwWidget w = handle; #ifdef USE_GDI if(handle->lowlevel->common.type == MwLLBackendGDI) { gdiopengl_t* o = handle->internal; o->wglMakeCurrent(NULL, NULL); DeleteDC(o->dc); o->wglDeleteContext(o->gl); MwDynamicClose(o->lib); } #endif #ifdef USE_X11 if(handle->lowlevel->common.type == MwLLBackendX11) { x11opengl_t* o = handle->internal; o->glXMakeCurrent(handle->lowlevel->x11.display, None, NULL); o->glXDestroyContext(handle->lowlevel->x11.display, o->gl); MwDynamicClose(o->lib); } #endif #ifdef USE_WAYLAND if(handle->lowlevel->common.type == MwLLBackendWayland) { /* todo */ } #endif while(w->parent != NULL) w = w->parent; w->berserk--; free(handle->internal); } static void mwOpenGLMakeCurrentImpl(MwWidget handle) { /* these swap interval functions belonging here actually stink! */ #ifdef USE_GDI if(handle->lowlevel->common.type == MwLLBackendGDI) { gdiopengl_t* o = handle->internal; BOOL(APIENTRY * swap_interval_ext)(int) = NULL; o->wglMakeCurrent(o->dc, o->gl); if((swap_interval_ext = MwOpenGLGetProcAddress(handle, "wglSwapIntervalEXT")) != NULL) { swap_interval_ext(1); } } #endif #ifdef USE_X11 if(handle->lowlevel->common.type == MwLLBackendX11) { x11opengl_t* o = handle->internal; void (*swap_interval_ext)(Display*, GLXDrawable, int) = NULL; void (*swap_interval_mesa)(unsigned int) = NULL; void (*swap_interval_sgi)(int) = NULL; o->glXMakeCurrent(handle->lowlevel->x11.display, handle->lowlevel->x11.window, o->gl); if((swap_interval_ext = MwOpenGLGetProcAddress(handle, "glXSwapIntervalEXT")) != NULL) { swap_interval_ext(handle->lowlevel->x11.display, handle->lowlevel->x11.window, 1); } if((swap_interval_mesa = MwOpenGLGetProcAddress(handle, "glXSwapIntervalMESA")) != NULL) { swap_interval_mesa(1); } if((swap_interval_sgi = MwOpenGLGetProcAddress(handle, "glXSwapIntervalSGI")) != NULL) { swap_interval_sgi(1); } } #endif #ifdef USE_WAYLAND if(handle->lowlevel->common.type == MwLLBackendWayland) { waylandopengl_t* o = handle->internal; if(!eglMakeCurrent(o->egl_display, o->egl_surface, o->egl_surface, o->egl_context)) { printf("ERROR: eglMakeCurrent, %0X\n", eglGetError()); } MwLLForceRender(handle->lowlevel); } #endif } static void mwOpenGLSwapBufferImpl(MwWidget handle) { #ifdef USE_GDI if(handle->lowlevel->common.type == MwLLBackendGDI) { gdiopengl_t* o = handle->internal; SwapBuffers(o->dc); } #endif #ifdef USE_X11 if(handle->lowlevel->common.type == MwLLBackendX11) { x11opengl_t* o = handle->internal; o->glXSwapBuffers(handle->lowlevel->x11.display, handle->lowlevel->x11.window); } #endif #ifdef USE_WAYLAND if(handle->lowlevel->common.type == MwLLBackendWayland) { waylandopengl_t* o = handle->internal; eglSwapInterval(o->egl_display, 0); if(!eglSwapBuffers(o->egl_display, o->egl_surface)) { printf("ERROR: eglSwapBuffers, %0X\n", eglGetError()); } struct gbm_bo* bo = o->gbm_surface_lock_front_buffer(o->gbm_surface); if(bo) { void* gbmBoMapData = NULL; // Needed for unmapping if gbm_bo_map is used MwU32 width = o->gbm_bo_get_width(bo); MwU32 height = o->gbm_bo_get_height(bo); MwU32 stride = o->gbm_bo_get_stride(bo); void* map = o->gbm_bo_map(bo, 0, 0, width, height, GBM_BO_TRANSFER_READ, &stride, &gbmBoMapData); MwU32 size = height * stride; if(map) { for(int y = 0; y < height; y++) { const MwU8* row = map + y * stride; for(int x = 0; x < width; x++) { /* Memory layout per pixel: [B][G][R][A] */ MwU8 b = row[x * 4 + 0]; MwU8 g = row[x * 4 + 1]; MwU8 r = row[x * 4 + 2]; int out = (y * width + x) * 4; o->frontbuffer_data[out + 0] = r; o->frontbuffer_data[out + 1] = g; o->frontbuffer_data[out + 2] = b; o->frontbuffer_data[out + 3] = 255; } } MwPixmapReloadRaw( o->frontbuffer, o->frontbuffer_data); } o->gbm_bo_unmap(bo, gbmBoMapData); } if(o->previous_bo) { o->gbm_surface_release_buffer(o->gbm_surface, o->previous_bo); } o->previous_bo = bo; MwForceRender(handle); } #endif } static void* mwOpenGLGetProcAddressImpl(MwWidget handle, const char* name) { #ifdef USE_GDI if(handle->lowlevel->common.type == MwLLBackendGDI) { gdiopengl_t* o = handle->internal; return o->wglGetProcAddress(name); } #endif #ifdef USE_X11 if(handle->lowlevel->common.type == MwLLBackendX11) { x11opengl_t* o = handle->internal; return o->glXGetProcAddress((const GLubyte*)name); } #endif #ifdef USE_WAYLAND if(handle->lowlevel->common.type == MwLLBackendWayland) { waylandopengl_t* o = handle->internal; return eglGetProcAddress(name); } #endif return NULL; } static void func_handler(MwWidget handle, const char* name, void* out, va_list va) { if(strcmp(name, "mwOpenGLMakeCurrent") == 0) { mwOpenGLMakeCurrentImpl(handle); } if(strcmp(name, "mwOpenGLSwapBuffer") == 0) { mwOpenGLSwapBufferImpl(handle); } if(strcmp(name, "mwOpenGLGetProcAddress") == 0) { const char* _name = va_arg(va, const char*); *(void**)out = mwOpenGLGetProcAddressImpl(handle, _name); } } static void frontbuffer_draw(MwWidget handle) { #ifdef USE_WAYLAND if(handle->lowlevel->common.type == MwLLBackendWayland) { waylandopengl_t* o = handle->internal; MwRect r; r.x = 0; r.y = 0; r.width = handle->lowlevel->wayland.ww; r.height = handle->lowlevel->wayland.wh; MwLLDrawPixmap(handle->lowlevel, &r, o->frontbuffer); } #endif } MwClassRec MwOpenGLClassRec = {wcreate, /* create */ destroy, /* destroy */ #ifdef USE_WAYLAND frontbuffer_draw, /* draw */ #else NULL, /* draw */ #endif NULL, /* click */ NULL, /* parent_resize */ NULL, /* prop_change */ NULL, /* mouse_move */ NULL, /* mouse_up */ NULL, /* mouse_down */ NULL, /* key */ func_handler, /* execute */ NULL, /* tick */ NULL, /* resize */ NULL, /* children_update */ NULL, /* children_prop_change */ NULL, /* clipboard */ NULL, /* props_change */ NULL, NULL, NULL}; MwClass MwOpenGLClass = &MwOpenGLClassRec; #else MWDECL MwClass MwOpenGLClass; MwClass MwOpenGLClass = NULL; #endif #endif