├── Makefile ├── README.md └── bcm_host.c /Makefile: -------------------------------------------------------------------------------- 1 | all: bcm_host.c 2 | gcc -c bcm_host.c -o bcm_host.o -fPIC `sdl-config --cflags` 3 | gcc -fPIC -shared -lEGL -lGLESv1_CM -Wl,-soname,libbcm_host.so.1 bcm_host.o -o libbcm_host.so.1.0 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Minecraft: Pi Edition wrapper 2 | ============================= 3 | 4 | This is a wrapper for Minecraft: Pi Edition's OpenGL ES drivers. It makes 5 | several assumptions about running on a Raspberry Pi, some of which are not 6 | valid on our system (Kosagi Novena). Namely, it suffers from the following: 7 | 8 | * Requires libbcm, which does not exist outside Broadcom chips 9 | * Does not pass the window object to eglContextCreate() 10 | * Calls init()/destroy() multiple times, which crashes our vendor's GL drivers 11 | * Links against libGLESv2.so but uses GLES 1.0 12 | 13 | This library and runtime environment aim to solve these problems. 14 | 15 | Usage 16 | ----- 17 | 18 | To compile our libbcm replacement, simply run "make". 19 | 20 | Then download Minecraft from http://pi.minecraft.net/ and extract it so 21 | that minecraft-pi is in the current directory. 22 | 23 | Copy /usr/lib/libGLESv1_CM.so or /usr/lib/arm-linux-gnueabihf/libGLESv1_CM.so 24 | to this directory and rename it to libGLESv2.so 25 | 26 | Finally, run minecraft-pi with the following invokation: 27 | 28 | LD_PRELOAD=./libbcm_host.so LD_LIBRARY_PATH=$PWD ./minecraft-pi 29 | 30 | Gotchas 31 | ------- 32 | 33 | Minecraft is run at full-screen for performance reasons. Unfortunately, 34 | the Minecraft program doesn't have an "Exit" function, so you'll have to 35 | come up with your own way of killing it. 36 | 37 | Design 38 | ------ 39 | 40 | There are counters that monitor how many times various functions are 41 | called. Minecraft seems to initialize EGL and then tear it down again 42 | right away, which causes our vendor's driver to crash. To get around 43 | this, we count how many times each driver is invoked, and only call 44 | the real egl functions after a certain number of invocations. 45 | -------------------------------------------------------------------------------- /bcm_host.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | void link_libglesv1_cm(void) __attribute__((constructor)); 10 | 11 | typedef void* DISPMANX_ELEMENT_HANDLE_T; 12 | typedef void* DISPMANX_DISPLAY_HANDLE_T; 13 | typedef void* DISPMANX_UPDATE_HANDLE_T; 14 | typedef void* DISPMANX_RESOURCE_HANDLE_T; 15 | typedef void* DISPMANX_PROTECTION_T; 16 | typedef void* VC_DISPMANX_ALPHA_T; 17 | typedef void* DISPMANX_CLAMP_T; 18 | typedef void* DISPMANX_TRANSFORM_T; 19 | typedef int VC_RECT_T; 20 | 21 | typedef struct { 22 | DISPMANX_ELEMENT_HANDLE_T element; 23 | int width; /* This is necessary because dispmanx elements are not queriable. */ 24 | int height; 25 | } EGL_DISPMANX_WINDOW_T; 26 | 27 | void *libXext; 28 | void *libGLESv1_CM; 29 | 30 | 31 | /* 32 | int gcoOS_DestroyContext() { 33 | printf("gcoOS_DestroyContext()\n"); 34 | return NULL; 35 | } 36 | */ 37 | 38 | static void *dlsym_assert(void *handle, const char *symbol) { 39 | void *ret; 40 | ret = dlsym(handle, symbol); 41 | if (!ret) { 42 | char err[2048]; 43 | snprintf(err, sizeof(err)-1, "Unable to get %s", symbol); 44 | perror(err); 45 | exit(1); 46 | } 47 | return ret; 48 | } 49 | 50 | static void *get_from_libEGL(const char *symbol) { 51 | static void *libEGL; 52 | if (!libEGL) { 53 | libEGL = dlopen("libEGL.so", RTLD_LAZY); 54 | if (!libEGL) { 55 | perror("Unable to open libEGL.so"); 56 | exit(1); 57 | } 58 | } 59 | return dlsym_assert(libEGL, symbol); 60 | } 61 | 62 | /* 63 | void *XextAddDisplay (void *extinfo, void *dpy, char *ext_name, 64 | void *hooks, int nevents, void *data) { 65 | void *result; 66 | static void *last_result; 67 | static void *(*XextAddDisplayReal) (void *extinfo, void *dpy, char *ext_name, 68 | void *hooks, int nevents, void *data); 69 | static int counter = 0; 70 | static int total_counter = 0; 71 | printf("XextAddDisplay(%p, %p, %s, %p, %d, %p) %d\n", extinfo, dpy, ext_name, hooks, nevents, data, total_counter++); 72 | 73 | if (!XextAddDisplayReal) { 74 | if (!libXext) { 75 | libXext = dlopen("libXext.so.6", RTLD_LAZY); 76 | if (!libXext) { 77 | perror("Unable to open libXext.so"); 78 | exit(1); 79 | } 80 | } 81 | XextAddDisplayReal = dlsym_assert(libXext, "XextAddDisplay"); 82 | } 83 | 84 | if (!strcmp(ext_name, "XFree86-DRI")) { 85 | counter++; 86 | if (counter == 1) { 87 | printf("Skipping!\n"); 88 | return last_result; 89 | } 90 | } 91 | result = XextAddDisplayReal(extinfo, dpy, ext_name, hooks, nevents, data); 92 | if (!strcmp(ext_name, "XFree86-DRI")) 93 | last_result = result; 94 | printf("Result of XextAddDisplayReal(): %p\n", result); 95 | return result; 96 | } 97 | */ 98 | 99 | EGLAPI EGLBoolean EGLAPIENTRY eglBindAPI(EGLenum api) 100 | { 101 | static EGLBoolean (*eglBindAPIReal)(EGLenum api); 102 | static EGLBoolean ret; 103 | static int counter = 0; 104 | 105 | fprintf(stderr, "eglBindAPI(%d): ", api); 106 | if (!eglBindAPIReal) 107 | eglBindAPIReal = get_from_libEGL("eglBindAPI"); 108 | 109 | switch(counter++) { 110 | default: 111 | ret = eglBindAPIReal(api); 112 | fprintf(stderr, "%d\n", ret); 113 | return ret; 114 | } 115 | } 116 | 117 | EGLAPI EGLBoolean EGLAPIENTRY eglTerminate(EGLDisplay dpy) 118 | { 119 | static EGLBoolean (*eglTerminateReal)(EGLDisplay dpy); 120 | static EGLBoolean ret; 121 | static int counter = 0; 122 | 123 | fprintf(stderr, "eglTerminateReal(%p): ", dpy); 124 | if (!eglTerminateReal) 125 | eglTerminateReal = get_from_libEGL("eglTerminate"); 126 | 127 | switch(counter++) { 128 | case 0: 129 | fprintf(stderr, "1 (lying)\n"); 130 | return 1; 131 | 132 | default: 133 | ret = eglTerminateReal(dpy); 134 | fprintf(stderr, "%d\n", ret); 135 | return ret; 136 | } 137 | } 138 | 139 | EGLAPI EGLBoolean EGLAPIENTRY eglDestroySurface(EGLDisplay dpy, EGLSurface surface) 140 | { 141 | static EGLBoolean (*eglDestroySurfaceReal)(EGLDisplay dpy, EGLSurface surface); 142 | static EGLBoolean ret; 143 | static int counter = 0; 144 | 145 | fprintf(stderr, "eglDestroySurface(%d, %d): ", dpy, surface); 146 | if (!eglDestroySurfaceReal) 147 | eglDestroySurfaceReal = get_from_libEGL("eglDestroySurface"); 148 | 149 | switch(counter++) { 150 | case 0: 151 | case 1: 152 | fprintf(stderr, "1 (lying)\n"); 153 | return 1; 154 | 155 | default: 156 | ret = eglDestroySurfaceReal(dpy, surface); 157 | fprintf(stderr, "%d\n", ret); 158 | return ret; 159 | } 160 | } 161 | 162 | EGLAPI EGLBoolean EGLAPIENTRY eglSwapBuffers(EGLDisplay dpy, EGLSurface surface) 163 | { 164 | static EGLBoolean (*eglSwapBuffersReal)(EGLDisplay dpy, EGLSurface surface); 165 | static EGLBoolean ret; 166 | static int counter = 0; 167 | 168 | // fprintf(stderr, "eglSwapBuffers(%p, %p): ", dpy, surface); 169 | if (!eglSwapBuffersReal) 170 | eglSwapBuffersReal = get_from_libEGL("eglSwapBuffers"); 171 | 172 | switch(counter++) { 173 | case 0: 174 | case 1: 175 | case 2: 176 | case 3: 177 | case 4: 178 | case 5: 179 | case 6: 180 | case 7: 181 | fprintf(stderr, "1 (guessed)\n"); 182 | return 1; 183 | 184 | default: 185 | ret = eglSwapBuffersReal(dpy, surface); 186 | //fprintf(stderr, "%d\n", ret); 187 | return ret; 188 | } 189 | } 190 | 191 | 192 | EGLAPI EGLContext EGLAPIENTRY eglCreateContext(EGLDisplay dpy, EGLConfig config, 193 | EGLContext share_context, 194 | const EGLint *attrib_list) 195 | { 196 | static EGLContext (*eglCreateContextReal)(EGLDisplay dpy, EGLConfig config, 197 | EGLContext share_context, 198 | const EGLint *attrib_list); 199 | static EGLContext *ret; 200 | static int counter = 0; 201 | 202 | fprintf(stderr, "eglCreateContext(%p, %p, %p, %p): ", dpy, config, share_context, attrib_list); 203 | if (!eglCreateContextReal) 204 | eglCreateContextReal = get_from_libEGL("eglCreateContext"); 205 | 206 | switch(counter++) { 207 | case 0: 208 | fprintf(stderr, "NULL (guessed)\n"); 209 | return NULL; 210 | 211 | case 1: 212 | ret = eglCreateContextReal(dpy, config, share_context, attrib_list); 213 | fprintf(stderr, "%p\n", ret); 214 | return ret; 215 | 216 | default: 217 | fprintf(stderr, "%p (cached)\n", ret); 218 | return ret; 219 | } 220 | } 221 | 222 | EGLAPI EGLBoolean EGLAPIENTRY eglChooseConfig(EGLDisplay dpy, const EGLint *attrib_list, 223 | EGLConfig *configs, EGLint config_size, 224 | EGLint *num_config) 225 | { 226 | static EGLBoolean (*eglChooseConfigReal)(EGLDisplay dpy, const EGLint *attrib_list, 227 | EGLConfig *configs, EGLint config_size, 228 | EGLint *num_config); 229 | static EGLBoolean ret; 230 | static int counter = 0; 231 | 232 | fprintf(stderr, "eglChooseConfig(%p, %p, %p, %d, %p): ", dpy, attrib_list, configs, config_size, num_config); 233 | if (!eglChooseConfigReal) 234 | eglChooseConfigReal = get_from_libEGL("eglChooseConfig"); 235 | 236 | switch(counter++) { 237 | case 0: 238 | fprintf(stderr, "1 (guessed)\n"); 239 | return 1; 240 | 241 | case 1: 242 | ret = eglChooseConfigReal(dpy, attrib_list, configs, config_size, num_config); 243 | fprintf(stderr, "%d\n", ret); 244 | return ret; 245 | 246 | default: 247 | fprintf(stderr, "%d (cached)\n", ret); 248 | return ret; 249 | } 250 | } 251 | 252 | 253 | EGLAPI EGLBoolean EGLAPIENTRY eglInitialize(EGLDisplay dpy, EGLint *major, EGLint *minor) 254 | { 255 | static EGLBoolean (*eglInitializeReal)(EGLDisplay dpy, EGLint *major, EGLint *minor); 256 | static EGLBoolean ret; 257 | static int counter = 0; 258 | fprintf(stderr, "eglInitialize(%p, %p, %p): ", dpy, major, minor); 259 | if (!eglInitializeReal) 260 | eglInitializeReal = get_from_libEGL("eglInitialize"); 261 | 262 | switch(counter++) { 263 | case 0: 264 | fprintf(stderr, "1 (guessed)\n"); 265 | return 1; 266 | 267 | case 1: 268 | ret = eglInitializeReal(dpy, major, minor); 269 | fprintf(stderr, "%d\n", ret); 270 | return ret; 271 | 272 | default: 273 | fprintf(stderr, "%d (cached)\n", ret); 274 | return ret; 275 | } 276 | } 277 | 278 | EGLAPI EGLBoolean EGLAPIENTRY eglMakeCurrent(EGLDisplay dpy, EGLSurface draw, 279 | EGLSurface read, EGLContext ctx) 280 | { 281 | static EGLBoolean (*eglMakeCurrentReal)(EGLDisplay dpy, EGLSurface draw, 282 | EGLSurface read, EGLContext ctx); 283 | static EGLBoolean ret; 284 | static int counter = 0; 285 | 286 | fprintf(stderr, "eglMakeCurrent(%p, %p, %p, %p): ", dpy, draw, read, ctx); 287 | if (!eglMakeCurrentReal) 288 | eglMakeCurrentReal = get_from_libEGL("eglMakeCurrent"); 289 | 290 | switch(counter++) { 291 | case 0: 292 | case 1: 293 | fprintf(stderr, "1 (guessed)\n"); 294 | return 1; 295 | 296 | case 2: 297 | ret = eglMakeCurrentReal(dpy, draw, read, ctx); 298 | fprintf(stderr, "%d\n", ret); 299 | return ret; 300 | 301 | default: 302 | fprintf(stderr, "%d (cached)\n", ret); 303 | return ret; 304 | } 305 | } 306 | 307 | EGLAPI EGLDisplay EGLAPIENTRY eglGetDisplay(EGLNativeDisplayType display_id) 308 | { 309 | static EGLDisplay (*eglGetDisplayReal)(EGLNativeDisplayType display_id); 310 | static EGLDisplay ret; 311 | static int counter = 0; 312 | 313 | fprintf(stderr, "eglGetDisplay(%d): ", display_id); 314 | if (!eglGetDisplayReal) 315 | eglGetDisplayReal = get_from_libEGL("eglGetDisplay"); 316 | 317 | switch(counter++) { 318 | case 0: 319 | fprintf(stderr, "4 (lying)\n"); 320 | return (void *)4; 321 | 322 | case 1: 323 | ret = eglGetDisplayReal(display_id); 324 | fprintf(stderr, "%p\n", ret); 325 | return ret; 326 | 327 | default: 328 | fprintf(stderr, "%p (cached)\n", ret); 329 | return ret; 330 | } 331 | } 332 | 333 | 334 | EGLAPI EGLSurface EGLAPIENTRY eglCreateWindowSurface(EGLDisplay dpy, EGLConfig config, 335 | EGLNativeWindowType win, 336 | const EGLint *attrib_list) { 337 | static int counter = 0; 338 | static EGLSurface (*eglCreateWindowSurfaceReal)(EGLDisplay dpy, EGLConfig config, 339 | EGLNativeWindowType win, 340 | const EGLint *attrib_list); 341 | static EGLSurface *ret; 342 | SDL_SysWMinfo sysInfo; //Will hold our Window information 343 | 344 | fprintf(stderr, "eglCreateWindowSurface(%p, %p, %p, %p): ", dpy, config, win, attrib_list); 345 | 346 | if (!eglCreateWindowSurfaceReal) 347 | eglCreateWindowSurfaceReal = get_from_libEGL("eglCreateWindowSurface"); 348 | 349 | switch(counter++) { 350 | case 0: 351 | fprintf(stderr, "0x4 (guessed)\n"); 352 | return (EGLSurface)0x4; 353 | 354 | case 1: 355 | SDL_VERSION(&sysInfo.version); //Set SDL version 356 | if(SDL_GetWMInfo(&sysInfo) <= 0) 357 | { 358 | fprintf(stderr, "Unable to get window handle"); 359 | return 0; 360 | } 361 | 362 | ret = eglCreateWindowSurfaceReal(dpy, config, (EGLNativeWindowType)sysInfo.info.x11.window, attrib_list); 363 | fprintf(stderr, "%p\n", ret); 364 | return ret; 365 | 366 | default: 367 | fprintf(stderr, "%p (cached)\n", ret); 368 | return ret; 369 | } 370 | } 371 | 372 | 373 | int32_t graphics_get_display_size( const uint16_t display_number, uint32_t *width, uint32_t *height) { 374 | printf("graphics_get_display_size(%d, %p, %p)\n", display_number, width, height); 375 | *width = 2560; 376 | *height = 1700; 377 | 378 | SDL_SetVideoMode(*width, *height, 32, SDL_FULLSCREEN);// | SDL_RESIZABLE); 379 | return 0; 380 | } 381 | 382 | int vc_dispmanx_display_open(int device) { 383 | printf("vc_dispmanx_display_open(%d)\n", device); 384 | return 0; 385 | } 386 | 387 | DISPMANX_ELEMENT_HANDLE_T vc_dispmanx_element_add ( DISPMANX_UPDATE_HANDLE_T update, 388 | DISPMANX_DISPLAY_HANDLE_T display, 389 | int32_t layer, const VC_RECT_T *dest_rect, DISPMANX_RESOURCE_HANDLE_T src, 390 | const VC_RECT_T *src_rect, DISPMANX_PROTECTION_T protection, 391 | VC_DISPMANX_ALPHA_T *alpha, 392 | DISPMANX_CLAMP_T *clamp, DISPMANX_TRANSFORM_T transform ) { 393 | printf("vc_dispmanx_element_add(%p, %p, %d, %p, %p, %p, %p, %p, %p, %p)\n", 394 | update, display, layer, dest_rect, src, src_rect, protection, 395 | alpha, clamp, transform); 396 | return NULL; 397 | } 398 | 399 | int vc_dispmanx_update_submit_sync( DISPMANX_UPDATE_HANDLE_T update ) { 400 | printf("vc_dispmanx_update_submit_sync(%p)\n", update); 401 | return 0; 402 | } 403 | 404 | void bcm_host_deinit(void) { 405 | printf("bcm_host_deinit()\n"); 406 | return; 407 | } 408 | 409 | int vc_dispmanx_update_start( int32_t priority ) { 410 | printf("vc_dispmanx_update_start(%d)\n", priority); 411 | return 0; 412 | } 413 | 414 | void bcm_host_init(void) { 415 | printf("bcm_host_init()\n"); 416 | return; 417 | } 418 | --------------------------------------------------------------------------------