/* * Copyright (c) 2001 Dan Gudmundsson * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * * $Id: esdl_driver.c,v 1.2 2006/05/15 14:45:44 dgud Exp $ */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include #include #ifdef _WIN32 #include #include #else #include #endif #ifdef _OSX_COCOA #include "CPS.h" #include "SDLMain.h" #include #include #endif #include #include #include #include "esdl.h" #define TEMP_BINARY_SIZE 512 static ErlDrvData sdl_driver_start(ErlDrvPort port, char *buff); static void sdl_driver_stop(ErlDrvData handle); static void sdl_driver_finish(void); static int sdl_driver_control(ErlDrvData handle, unsigned int command, char* buf, int count, char** res, int res_size); static void standard_outputv(ErlDrvData drv_data, ErlIOVec *ev); /* ** The driver struct */ static ErlDrvEntry sdl_driver_entry = { NULL, /* F_PTR init, N/A */ sdl_driver_start, /* L_PTR start, called when port is opened */ sdl_driver_stop, /* F_PTR stop, called when port is closed */ NULL, /* F_PTR output, called when erlang has sent */ NULL, /* F_PTR ready_input, called when input descriptor ready */ NULL, /* F_PTR ready_output, called when output descriptor ready */ "sdl_driver", /* char *driver_name, the argument to open_port */ sdl_driver_finish, /* F_PTR finish, called when unloaded */ NULL, /* void * that is not used (BC) */ sdl_driver_control, /* F_PTR control, port_control callback */ NULL, /* F_PTR timeout, reserved */ standard_outputv, /* F_PTR outputv, reserved */ }; DRIVER_INIT(sdl_driver) { return &sdl_driver_entry; } static ErlDrvData sdl_driver_start(ErlDrvPort port, char *buff) { sdl_data *data; #ifdef _OSX_COCOA CPSProcessSerNum PSN; OSErr err; #endif /* _OSX_COCOA */ #ifdef _WIN32 if ( SDL_RegisterApp("Erlang SDL", CS_BYTEALIGNCLIENT, GetModuleHandle(NULL)) < 0 ) { fprintf(stderr, "WinMain() error %s \n", SDL_GetError()); return(ERL_DRV_ERROR_GENERAL); } #endif /* _WIN32 */ data = malloc(sizeof(sdl_data)); if (data == NULL) { fprintf(stderr, " Couldn't alloc mem\r\n"); return(ERL_DRV_ERROR_GENERAL); /* ENOMEM */ } else { set_port_control_flags(port, PORT_CONTROL_FLAG_BINARY); data->driver_data = port; // data->fns = init_fps(); data->op = 0; data->len = 0; data->buff = NULL; data->temp_bin = driver_alloc_binary(TEMP_BINARY_SIZE); data->next_bin = 0; init_fps(data); #ifdef _OSX_COCOA // Removes the warnings about memory leakage // (not sure it actually works, though) data->release_pool = objc_msgSend(objc_msgSend(objc_getClass("NSAutoreleasePool"), @selector(alloc)), @selector(init)); data->app = objc_msgSend(objc_getClass("NSApplication"), @selector(sharedApplication)); // To get a Menu & a dock icon : err = CPSGetCurrentProcess(&PSN); assert(!(err = CPSSetProcessName(&PSN,"ESDL"))); assert(!(err = CPSEnableForegroundOperation(&PSN,0x03,0x3C,0x2C,0x1103))); assert(!(err = CPSSetFrontProcess(&PSN))); // Makes the SDL window respond to keyboard events (???) //[NSAppleEventManager sharedAppleEventManager]; #endif /* _OSX_COCOA */ } return (ErlDrvData) data; } static void sdl_driver_stop(ErlDrvData handle) { sdl_data *sd = ((sdl_data *)handle); SDL_Quit(); free(sd->fun_tab); free(sd->str_tab); #ifdef _WIN32 UnregisterClass("Erlang SDL", GetModuleHandle(NULL)); #endif /* _WIN32 */ free(handle); } static void sdl_driver_finish(void) { } static int sdl_driver_control(ErlDrvData handle, unsigned op, char* buf, int count, char** res, int res_size) { sdl_data* sd = (sdl_data *) handle; sdl_fun func; sd->buff = NULL; sd->len = 0; sd->op = op; func = sd->fun_tab[op]; func(sd, count, buf); (*res) = sd->buff; return sd->len; } static int sdl_driver_debug_control(ErlDrvData handle, unsigned op, char* buf, int count, char** res, int res_size) { sdl_data* sd = (sdl_data *) handle; sdl_fun func; int len; sd->buff = NULL; sd->len = 0; sd->op = op; fprintf(stderr, "Command:%d:%s: ", op, sd->str_tab[op]); func = sd->fun_tab[op]; func(sd, count, buf); if ((len = sd->len) >= 0) { fprintf(stderr, "ok\r\n"); (*res) = sd->buff; return len; } else { fprintf(stderr, "error\r\n"); *res = 0; return -1; } } void sdl_send(sdl_data *sd, int len) { if (sd->buff == NULL) { fprintf(stderr, "ESDL INTERNAL ERROR: sdl_send in %s sent NULL buffer: %d\r\n", sd->str_tab[sd->op], len); abort(); } if (len > sd->len) { fprintf(stderr, "ESDL INTERNAL ERROR: sdl_send in %s allocated %d sent %d\r\n", sd->str_tab[sd->op], sd->len, len); abort(); } /* Workaround that driver_control doesn't check length */ ((ErlDrvBinary *) sd->buff)->orig_size = len; sd->len = len; } char* sdl_getbuff(sdl_data *sd, int size) { ErlDrvBinary* bin; sd->len = size; bin = driver_alloc_binary(size); sd->buff = bin; /* And return the pointer to the bytes */ return bin->orig_bytes; } char* sdl_get_temp_buff(sdl_data* sd, int size) { if (size > TEMP_BINARY_SIZE) { return sdl_getbuff(sd, size); } else { ErlDrvBinary* bin = (ErlDrvBinary *) sd->temp_bin; driver_binary_inc_refc(bin); sd->buff = bin; sd->len = size; return bin->orig_bytes; } } void sdl_util_debug(sdl_data *sd, int len, char* bp) { if (*bp) { sdl_driver_entry.control = sdl_driver_debug_control; } else { sdl_driver_entry.control = sdl_driver_control; } } static void standard_outputv(ErlDrvData drv_data, ErlIOVec* ev) { sdl_data* sd = (sdl_data *) drv_data; ErlDrvBinary* bin; if (ev->vsize == 2) { int i = sd->next_bin; sd->bin[i].base = ev->iov[1].iov_base; sd->bin[i].size = ev->iov[1].iov_len; bin = ev->binv[1]; driver_binary_inc_refc(bin); /* Otherwise it could get deallocated */ sd->bin[i].bin = bin; sd->next_bin++; } } void sdl_free_binaries(sdl_data* sd) { int i; for (i = sd->next_bin - 1; i >= 0; i--) { driver_free_binary(sd->bin[i].bin); } sd->next_bin = 0; }