/* vim:expandtab:ts=2 sw=2: */ /* Grafx2 - The Ultimate 256-color bitmap paint program Copyright 2009-2011 Adrien Destugues Grafx2 is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. Grafx2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Grafx2; if not, see */ /*! \file factory.c * \brief Brush factory - generates brush from lua scripts * * The brush factory allows you to generate brushes with Lua code. */ #include #include "brush.h" #include "buttons.h" #include "engine.h" #include "errors.h" #include "filesel.h" // Get_item_by_index #include "fileseltools.h" #include "global.h" #include "graph.h" #include "io.h" // find_last_separator #include "misc.h" #include "osdep.h" #include "pages.h" // Backup() #include "readline.h" #include "screen.h" #include "windows.h" #include "palette.h" #include "input.h" // Is_shortcut() #include "help.h" // Window_help() #include "graph.h" #include "filesel.h" // Read_list_of_drives() #include "realpath.h" #include "setup.h" #include "tiles.h" /// Lua scripts bound to shortcut keys. char * Bound_script[10]; #ifdef __ENABLE_LUA__ #include #include #include #include // for DBL_MAX #include //for INT_MIN #include // strncpy() #include // for atof() #if defined(_MSC_VER) #define strdup _strdup #define putenv _putenv #endif #include "unicode.h" #include "gfx2mem.h" /// /// Number of characters for name in fileselector. /// Window is adjusted according to it. #define NAME_WIDTH 34 /// Number of characters for the description block #define DESC_WIDTH ((NAME_WIDTH+2)*8/6) /// Position of fileselector top, in window space #define FILESEL_Y 30 // Work data that can be used during a script static byte * Brush_backup = NULL; static word Brush_backup_width; static word Brush_backup_height; static byte Palette_has_changed; static byte Brush_was_altered; static byte Original_fore_color; static byte Original_back_color; static byte Is_backed_up; static T_Page * Main_backup_page; static byte * Main_backup_screen; static byte Cursor_is_visible; static byte Window_needs_update; /// Helper function to clamp a double to 0-255 range static byte clamp_byte(double value) { if (value<0.0) return 0; else if (value>255.0) return 255; else return (byte)value; } /// /// This macro reads a Lua argument into a double or integral lvalue. /// If argument is invalid, it will break the caller and raise a verbose message. /// This macro uses 2 existing symbols: L for the context, and nb_args=lua_gettop(L) /// @param index Index of the argument to check, starting at 1. /// @param func_name The name of the lua callback, to display a message in case of error. /// @param dest Destination lvalue. Can be a double, or any integral type. Conversion will "floor". /// @param min_value Check for minimum value. Pass a double, or if you don't care, -DBL_MAX. /// @param max_value Check for maximum value. Pass a double, or if you don't care, DBL_MAX. #define LUA_ARG_NUMBER(index, func_name, dest, min_value, max_value) \ do { \ double value; \ if (nb_args < (index)) return luaL_error(L, "%s: Argument %d is missing.", func_name, (index)); \ if (!lua_isnumber(L, (index))) return luaL_error(L, "%s: Argument %d is not a number.", func_name, (index)); \ value = lua_tonumber(L, (index)); \ if ((min_value) != -DBL_MAX && value<(min_value)) return luaL_error(L, "%s: Argument %d was too small, it had value of %f and minimum should be %f.", func_name, (index), value, (double)(min_value)); \ if ((max_value) != DBL_MAX && value>(max_value)) return luaL_error(L, "%s: Argument %d was too big, it had value of %f and maximum should be %f.", func_name, (index), value, (double)(max_value)); \ dest = value; \ } while(0) /// /// This macro reads a Lua argument into a string. /// If argument is invalid, it will break the caller and raise a verbose message. /// This macro uses 2 existing symbols: L for the context, and nb_args=lua_gettop(L) /// @param index Index of the argument to check, starting at 1. /// @param func_name The name of the lua callback, to display a message in case of error. /// @param dest Destination string pointer, ideally a const char *. #define LUA_ARG_STRING(index, func_name, dest) \ do { \ if (nb_args < (index)) return luaL_error(L, "%s: Argument %d is missing.", func_name, index); \ if (!lua_isstring(L, (index))) return luaL_error(L, "%s: Argument %d is not a string.", func_name, index); \ dest = lua_tostring(L, (index)); \ } while (0) /// /// This macro reads a Lua argument into a boolean. /// If argument is invalid, it will break the caller and raise a verbose message. /// This macro uses 2 existing symbols: L for the context, and nb_args=lua_gettop(L) /// @param index Index of the argument to check, starting at 1. /// @param func_name The name of the lua callback, to display a message in case of error. /// @param dest Destination variable, any numeric type. #define LUA_ARG_BOOL(index, func_name, dest) \ do { \ if (nb_args < (index)) return luaL_error(L, "%s: Argument %d is missing.", func_name, index); \ if (!lua_isboolean(L, (index))) return luaL_error(L, "%s: Argument %d is not a boolean.", func_name, index); \ dest = lua_toboolean(L, (index)); \ } while (0) /// /// This macro checks that a Lua argument is a function. /// If argument is invalid, it will break the caller and raise a verbose message. /// This macro uses 2 existing symbols: L for the context, and nb_args=lua_gettop(L) /// @param index Index of the argument to check, starting at 1. /// @param func_name The name of the lua callback, to display a message in case of error. #define LUA_ARG_FUNCTION(index, func_name) \ do { \ if (nb_args < (index)) return luaL_error(L, "%s: Argument %d is missing.", func_name, index); \ if (!lua_isfunction(L, (index))) return luaL_error(L, "%s: Argument %d is not a function.", func_name, index); \ } while (0) /// Check if 'num' arguments were provided exactly #define LUA_ARG_LIMIT(num, func_name) \ do { \ if (nb_args != (num)) \ return luaL_error(L, "%s: Expected %d arguments, but found %d.", func_name, (num), nb_args); \ } while(0) /// Declares a function in the form BIND_unsaved() : for example L_PutPicturePixel_unsaved() #define DECLARE_UNSAVED(BIND) \ int BIND ## _unsaved(lua_State* L) \ { \ Backup_if_necessary(L, Main.current_layer); \ Register_main_writable(L); \ return BIND(L); \ } // forward declarations void Register_main_readonly(lua_State* L); void Register_main_writable(lua_State* L); int L_SetColor(lua_State* L); int L_SetColor_unsaved(lua_State* L); // const char * Lua_version(void) { // LUA_RELEASE is only available since 5.2+, with format "Lua x.y.z" // The only version information available since Lua 5.0 is LUA_VERSION, // with the format "Lua x.y" #if defined(LUA_RELEASE) return LUA_RELEASE; #elif defined (LUA_VERSION) return LUA_VERSION; #else return "Unknown"; #endif } // Updates the screen colors after a running screen has modified the palette. void Update_colors_during_script(void) { if (Palette_has_changed) { Set_palette(Main.palette); Compute_optimal_menu_colors(Main.palette); Display_menu(); Palette_has_changed=0; } } /// Paint a pixel in image without updating the screen static void Pixel_figure_no_screen(word x_pos, word y_pos, byte color) { if (x_pos < Main.image_width && y_pos < Main.image_height) Pixel_in_current_screen(x_pos, y_pos, color); } void Backup_if_necessary(lua_State* L, int layer) { if (!Is_backed_up) { Backup_layers(layer); Is_backed_up = 1; if (layer == Main.current_layer) { Main_backup_page = Main.backups->Pages->Next; Main_backup_screen = Screen_backup; Register_main_writable(L); } } else { if (layer == LAYER_NONE) { // Already OK return; } if (Dup_layer_if_shared(Main.backups->Pages, layer)) { // Depth buffer etc to modify too ? Register_main_writable(L); } } } // Wrapper functions to call C from Lua int L_SetBrushSize(lua_State* L) { int i; int w; int h; int nb_args=lua_gettop(L); LUA_ARG_LIMIT (2, "setbrushsize"); LUA_ARG_NUMBER(1, "setbrushsize", w, 1, 10000); LUA_ARG_NUMBER(2, "setbrushsize", h, 1, 10000); if (Realloc_brush(w, h, NULL, NULL)) { return luaL_error(L, "setbrushsize: Resize failed"); } Brush_was_altered=1; // Fill with Back_color memset(Brush_original_pixels,Back_color,(long)Brush_width*Brush_height); memset(Brush,Back_color,(long)Brush_width*Brush_height); // Adopt the current palette. memcpy(Brush_original_palette, Main.palette,sizeof(T_Palette)); for (i=0; i<256; i++) Brush_colormap[i]=i; //-- // Center the handle Brush_offset_X=(Brush_width>>1); Brush_offset_Y=(Brush_height>>1); return 0; } int L_GetBrushSize(lua_State* L) { lua_pushinteger(L, Brush_width); lua_pushinteger(L, Brush_height); return 2; } int L_PutBrushPixel(lua_State* L) { int x; int y; uint8_t c; int nb_args=lua_gettop(L); LUA_ARG_LIMIT (3, "putbrushpixel"); LUA_ARG_NUMBER(1, "putbrushpixel", x, INT_MIN, INT_MAX); LUA_ARG_NUMBER(2, "putbrushpixel", y, INT_MIN, INT_MAX); LUA_ARG_NUMBER(3, "putbrushpixel", c, INT_MIN, INT_MAX); if (!Brush_was_altered) { int i; // First time writing in brush: // Adopt the current palette. memcpy(Brush_original_palette, Main.palette,sizeof(T_Palette)); memcpy(Brush_original_pixels, Brush, Brush_width*Brush_height); for (i=0; i<256; i++) Brush_colormap[i]=i; //-- Brush_was_altered=1; } if (x<0 || y<0 || x>=Brush_width || y>=Brush_height) ; else { Pixel_in_brush(x, y, c); } return 0; // no values returned for lua } int L_GetBrushPixel(lua_State* L) { int x; int y; uint8_t c; int nb_args=lua_gettop(L); LUA_ARG_LIMIT (2, "getbrushpixel"); LUA_ARG_NUMBER(1, "getbrushpixel", x, INT_MIN, INT_MAX); LUA_ARG_NUMBER(2, "getbrushpixel", y, INT_MIN, INT_MAX); if (x<0 || y<0 || x>=Brush_width || y>=Brush_height) { c = Back_color; // Return 'transparent' } else { c = Read_pixel_from_brush(x, y); } lua_pushinteger(L, c); return 1; } int L_GetBrushBackupPixel(lua_State* L) { int x; int y; uint8_t c; int nb_args=lua_gettop(L); LUA_ARG_LIMIT (2, "getbrushbackuppixel"); LUA_ARG_NUMBER(1, "getbrushbackuppixel", x, INT_MIN, INT_MAX); LUA_ARG_NUMBER(2, "getbrushbackuppixel", y, INT_MIN, INT_MAX); if (x<0 || y<0 || x>=Brush_backup_width || y>=Brush_backup_height) { c = Back_color; // Return 'transparent' } else { c = *(Brush_backup + y * Brush_backup_width + x); } lua_pushinteger(L, c); return 1; } int L_SetPictureSize(lua_State* L) { int w; int h; int nb_args=lua_gettop(L); int i; LUA_ARG_LIMIT (2, "setpicturesize"); LUA_ARG_NUMBER(1, "setpicturesize", w, 1, 9999); LUA_ARG_NUMBER(2, "setpicturesize", h, 1, 9999); if (w == Main.image_width && h == Main.image_height) { // nothing to do at all return 0; } if (Is_backed_up) { // Script has already modified pixels or palette : // Resize without counting an additional Undo step. Backup_in_place(w, h); } else { // Script has not modified the image/palette yet, so it's not backed up: // Make a backup that counts as a new Undo step. Upload_infos_page(&Main); Backup_with_new_dimensions(w, h); } // part of Resize_image() : the pixel copy part. for (i=0; iPages->Nb_layers; i++) { Copy_part_of_image_to_another( Main.backups->Pages->Next->Image[i].Pixels,0,0,Min(Main.backups->Pages->Next->Width,Main.image_width), Min(Main.backups->Pages->Next->Height,Main.image_height),Main.backups->Pages->Next->Width, Main.backups->Pages->Image[i].Pixels,0,0,Main.image_width); } Redraw_layered_image(); Is_backed_up = 1; Main_backup_page = Main.backups->Pages->Next; Main_backup_screen = Screen_backup; Register_main_writable(L); lua_register(L,"setcolor",L_SetColor); return 0; } int L_SetSparePictureSize(lua_State* L) { int w; int h; int nb_args=lua_gettop(L); int i; LUA_ARG_LIMIT (2, "setsparepicturesize"); LUA_ARG_NUMBER(1, "setsparepicturesize", w, 1, 9999); LUA_ARG_NUMBER(2, "setsparepicturesize", h, 1, 9999); Backup_and_resize_the_spare(w, h); // part of Resize_image() : the pixel copy part. for (i=0; iPages->Nb_layers; i++) { Copy_part_of_image_to_another( Spare.backups->Pages->Next->Image[i].Pixels,0,0,Min(Spare.backups->Pages->Next->Width,Spare.image_width), Min(Spare.backups->Pages->Next->Height,Spare.image_height),Spare.backups->Pages->Next->Width, Spare.backups->Pages->Image[i].Pixels,0,0,Spare.image_width); } Redraw_spare_image(); return 0; } int L_GetPictureSize(lua_State* L) { lua_pushinteger(L, Main.image_width); lua_pushinteger(L, Main.image_height); return 2; } int L_ClearPicture(lua_State* L) { int c; int nb_args=lua_gettop(L); LUA_ARG_LIMIT (1, "clearpicture"); LUA_ARG_NUMBER(1, "clearpicture", c, INT_MIN, INT_MAX); if (Stencil_mode && Config.Clear_with_stencil) Clear_current_image_with_stencil(c,Stencil); else Clear_current_image(c); Redraw_layered_image(); return 0; // no values returned for lua } int L_PutPicturePixel(lua_State* L) { int x; int y; int c; int nb_args=lua_gettop(L); LUA_ARG_LIMIT (3, "putpicturepixel"); LUA_ARG_NUMBER(1, "putpicturepixel", x, INT_MIN, INT_MAX); LUA_ARG_NUMBER(2, "putpicturepixel", y, INT_MIN, INT_MAX); LUA_ARG_NUMBER(3, "putpicturepixel", c, INT_MIN, INT_MAX); // Bound check if (x<0 || y<0 || x>=Main.image_width || y>=Main.image_height) { // Silently ignored return 0; } Pixel_in_current_screen(x, y, c); return 0; // no values returned for lua } int L_PutSparePicturePixel(lua_State* L) { int x; int y; int c; int nb_args=lua_gettop(L); LUA_ARG_LIMIT (3, "putsparepicturepixel"); LUA_ARG_NUMBER(1, "putsparepicturepixel", x, INT_MIN, INT_MAX); LUA_ARG_NUMBER(2, "putsparepicturepixel", y, INT_MIN, INT_MAX); LUA_ARG_NUMBER(3, "putsparepicturepixel", c, INT_MIN, INT_MAX); // Bound check if (x<0 || y<0 || x>=Spare.image_width || y>=Spare.image_height) { // Silently ignored return 0; } Pixel_in_spare(x, y, c); return 0; // no values returned for lua } int L_DrawLine(lua_State* L) { int x1, y1, x2, y2, c; int nb_args = lua_gettop(L); LUA_ARG_LIMIT(5, "drawline"); LUA_ARG_NUMBER(1, "drawline", x1, INT_MIN, INT_MAX); LUA_ARG_NUMBER(2, "drawline", y1, INT_MIN, INT_MAX); LUA_ARG_NUMBER(3, "drawline", x2, INT_MIN, INT_MAX); LUA_ARG_NUMBER(4, "drawline", y2, INT_MIN, INT_MAX); LUA_ARG_NUMBER(5, "drawline", c, INT_MIN, INT_MAX); Set_Pixel_figure(Pixel_figure_no_screen); Draw_line_general(x1, y1, x2, y2, c); return 0; } int L_DrawFilledRect(lua_State* L) { int x1, y1, x2, y2, c; int min_x,min_y,max_x,max_y, x_pos, y_pos; int nb_args = lua_gettop(L); LUA_ARG_LIMIT(5, "drawfilledrect"); LUA_ARG_NUMBER(1, "drawfilledrect", x1, INT_MIN, INT_MAX); LUA_ARG_NUMBER(2, "drawfilledrect", y1, INT_MIN, INT_MAX); LUA_ARG_NUMBER(3, "drawfilledrect", x2, INT_MIN, INT_MAX); LUA_ARG_NUMBER(4, "drawfilledrect", y2, INT_MIN, INT_MAX); LUA_ARG_NUMBER(5, "drawfilledrect", c, INT_MIN, INT_MAX); // Put bounds in ascending order if (x2>x1) { min_x=x1; max_x=x2; } else { min_x=x2; max_x=x1; } if (y2>y1) { min_y=y1; max_y=y2; } else { min_y=y2; max_y=y1; } // Clipping limits if (max_x>Main.image_width) max_x=Main.image_width-1; if (max_y>Main.image_height) max_y=Main.image_height-1; if (min_x<0) min_x=0; if (min_y<0) min_y=0; // Perform drawing for (y_pos=min_y; y_pos<=max_y;y_pos++) for (x_pos=min_x; x_pos<=max_x;x_pos++) Pixel_in_current_screen(x_pos,y_pos,c); return 0; } int L_DrawCircle(lua_State* L) { int x1, y1, r, c; int nb_args = lua_gettop(L); LUA_ARG_LIMIT(4, "drawcircle"); LUA_ARG_NUMBER(1, "drawcircle", x1, INT_MIN, INT_MAX); LUA_ARG_NUMBER(2, "drawcircle", y1, INT_MIN, INT_MAX); LUA_ARG_NUMBER(3, "drawcircle", r, INT_MIN, INT_MAX); LUA_ARG_NUMBER(4, "drawcircle", c, INT_MIN, INT_MAX); Set_Pixel_figure(Pixel_figure_no_screen); Draw_empty_circle_general(x1, y1, r*r, c); return 0; } int L_DrawDisk(lua_State* L) { int center_x, center_y, diameter, c, r, even; long circle_limit; short x_pos,y_pos; short min_x,max_x,min_y,max_y; double r_float; int nb_args = lua_gettop(L); LUA_ARG_LIMIT(4, "drawdisk"); LUA_ARG_NUMBER(1, "drawdisk", center_x, INT_MIN, INT_MAX); LUA_ARG_NUMBER(2, "drawdisk", center_y, INT_MIN, INT_MAX); LUA_ARG_NUMBER(3, "drawdisk", r_float, INT_MIN, INT_MAX); LUA_ARG_NUMBER(4, "drawdisk", c, INT_MIN, INT_MAX); if (r_float<0.0) return 0; diameter=(int)(floor(r_float*2.0+1.0)); r=diameter/2; even=!(diameter&1); circle_limit = Circle_squared_diameter(diameter); // Compute clipping limits min_x=center_x-r+even<0 ? 0 : center_x-r+even; max_x=center_x+r>=Main.image_width? Main.image_width-1 : center_x+r; min_y=center_y-r+even<0 ? 0 : center_y-r+even; max_y=center_y+r>=Main.image_height? Main.image_height-1 : center_y+r; for (y_pos=min_y;y_pos<=max_y;y_pos++) { short y=(y_pos-center_y)*2-even; for (x_pos=min_x;x_pos<=max_x;x_pos++) { short x=(x_pos-center_x)*2-even; if (x*x+y*y <= circle_limit) Pixel_in_current_screen(x_pos,y_pos,c); } } return 0; } int L_GetPicturePixel(lua_State* L) { int x; int y; int nb_args=lua_gettop(L); LUA_ARG_LIMIT (2, "getpicturepixel"); LUA_ARG_NUMBER(1, "getpicturepixel", x, INT_MIN, INT_MAX); LUA_ARG_NUMBER(2, "getpicturepixel", y, INT_MIN, INT_MAX); // Bound check if (x<0 || y<0 || x>=Main.image_width || y>=Main.image_height) { // Silently return the image's transparent color lua_pushinteger(L, Main.backups->Pages->Transparent_color); return 1; } lua_pushinteger(L, Read_pixel_from_current_screen(x,y)); return 1; } int L_GetBackupPixel(lua_State* L) { int x; int y; int nb_args=lua_gettop(L); LUA_ARG_LIMIT (2, "getbackuppixel"); LUA_ARG_NUMBER(1, "getbackuppixel", x, INT_MIN, INT_MAX); LUA_ARG_NUMBER(2, "getbackuppixel", y, INT_MIN, INT_MAX); // Bound check if (x<0 || y<0 || x>=Main_backup_page->Width || y>=Main_backup_page->Height) { // Silently return the image's transparent color lua_pushinteger(L, Main_backup_page->Transparent_color); return 1; } // Can't use Read_pixel_from_backup_screen(), because in a Lua script // the "backup" can use a different screen dimension. lua_pushinteger(L, *(Main_backup_screen + x + Main_backup_page->Width * y)); return 1; } int L_GetLayerPixel(lua_State* L) { int x; int y; int nb_args=lua_gettop(L); LUA_ARG_LIMIT (2, "getlayerpixel"); LUA_ARG_NUMBER(1, "getlayerpixel", x, INT_MIN, INT_MAX); LUA_ARG_NUMBER(2, "getlayerpixel", y, INT_MIN, INT_MAX); // Bound check if (x<0 || y<0 || x>=Main.image_width || y>=Main.image_height) { // Silently return the image's transparent color lua_pushinteger(L, Main.backups->Pages->Transparent_color); return 1; } lua_pushinteger(L, Read_pixel_from_current_layer(x,y)); return 1; } // Spare int L_GetSparePictureSize(lua_State* L) { lua_pushinteger(L, Spare.image_width); lua_pushinteger(L, Spare.image_height); return 2; } int L_GetSpareLayerPixel(lua_State* L) { int x; int y; int nb_args=lua_gettop(L); LUA_ARG_LIMIT (2, "getsparelayerpixel"); LUA_ARG_NUMBER(1, "getsparelayerpixel", x, INT_MIN, INT_MAX); LUA_ARG_NUMBER(2, "getsparelayerpixel", y, INT_MIN, INT_MAX); // Bound check if (x<0 || y<0 || x>=Spare.image_width || y>=Spare.image_height) { // Silently return the image's transparent color lua_pushinteger(L, Spare.backups->Pages->Transparent_color); return 1; } lua_pushinteger(L, *(Spare.backups->Pages->Image[Spare.current_layer].Pixels + y*Spare.image_width + x)); return 1; } int L_GetSparePicturePixel(lua_State* L) { int x; int y; int nb_args=lua_gettop(L); LUA_ARG_LIMIT (2, "getsparepicturepixel"); LUA_ARG_NUMBER(1, "getsparepicturepixel", x, INT_MIN, INT_MAX); LUA_ARG_NUMBER(2, "getsparepicturepixel", y, INT_MIN, INT_MAX); // Some bound checking is done by the function itself, here's the rest. if (x<0 || y<0) { // Silently return the image's transparent color lua_pushinteger(L, Spare.backups->Pages->Transparent_color); return 1; } lua_pushinteger(L, Read_pixel_from_spare_screen(x,y)); return 1; } int L_GetSpareColor(lua_State* L) { byte c; int nb_args=lua_gettop(L); LUA_ARG_LIMIT (1, "getsparecolor"); LUA_ARG_NUMBER(1, "getsparecolor", c, INT_MIN, INT_MAX); lua_pushinteger(L, Spare.palette[c].R); lua_pushinteger(L, Spare.palette[c].G); lua_pushinteger(L, Spare.palette[c].B); return 3; } int L_GetSpareTransColor(lua_State* L) { lua_pushinteger(L, Spare.backups->Pages->Transparent_color); return 1; } int L_SetColor(lua_State* L) { byte c; double r, g, b; int nb_args=lua_gettop(L); LUA_ARG_LIMIT (4, "setcolor"); LUA_ARG_NUMBER(1, "setcolor", c, INT_MIN, INT_MAX); LUA_ARG_NUMBER(2, "setcolor", r, INT_MIN, INT_MAX); LUA_ARG_NUMBER(3, "setcolor", g, INT_MIN, INT_MAX); LUA_ARG_NUMBER(4, "setcolor", b, INT_MIN, INT_MAX); Main.palette[c].R=Round_palette_component(clamp_byte(r)); Main.palette[c].G=Round_palette_component(clamp_byte(g)); Main.palette[c].B=Round_palette_component(clamp_byte(b)); // Set_color(c, r, g, b); Not needed. Update screen when script is finished Palette_has_changed=1; return 0; } int L_SetColor_unsaved(lua_State* L) { Backup_if_necessary(L, LAYER_NONE); lua_register(L,"setcolor",L_SetColor); return L_SetColor(L); } int L_GetColor(lua_State* L) { byte c; int nb_args=lua_gettop(L); LUA_ARG_LIMIT (1, "getcolor"); LUA_ARG_NUMBER(1, "getcolor", c, INT_MIN, INT_MAX); lua_pushinteger(L, Main.palette[c].R); lua_pushinteger(L, Main.palette[c].G); lua_pushinteger(L, Main.palette[c].B); return 3; } int L_GetBackupColor(lua_State* L) { byte c; int nb_args=lua_gettop(L); LUA_ARG_LIMIT (1, "getbackupcolor"); LUA_ARG_NUMBER(1, "getbackupcolor", c, INT_MIN, INT_MAX); lua_pushinteger(L, Main_backup_page->Palette[c].R); lua_pushinteger(L, Main_backup_page->Palette[c].G); lua_pushinteger(L, Main_backup_page->Palette[c].B); return 3; } int L_MatchColor(lua_State* L) { double r, g, b; int c; int nb_args=lua_gettop(L); LUA_ARG_LIMIT (3, "matchcolor"); LUA_ARG_NUMBER(1, "matchcolor", r, -DBL_MAX, DBL_MAX); LUA_ARG_NUMBER(2, "matchcolor", g, -DBL_MAX, DBL_MAX); LUA_ARG_NUMBER(3, "matchcolor", b, -DBL_MAX, DBL_MAX); c = Best_color_nonexcluded(clamp_byte(r),clamp_byte(g),clamp_byte(b)); lua_pushinteger(L, c); return 1; } int L_MatchColor2(lua_State* L) { double r, g, b; double l_weight = 0.25; byte best_color = 0; int nb_args=lua_gettop(L); if (nb_args < 3 || nb_args > 4) { return luaL_error(L, "matchcolor2: Expected 3 or 4 arguments, but found %d.", nb_args); } LUA_ARG_NUMBER(1, "matchcolor2", r, -DBL_MAX, DBL_MAX); LUA_ARG_NUMBER(2, "matchcolor2", g, -DBL_MAX, DBL_MAX); LUA_ARG_NUMBER(3, "matchcolor2", b, -DBL_MAX, DBL_MAX); if (nb_args > 3) { LUA_ARG_NUMBER(4, "matchcolor2", l_weight, 0.0, 1.0); } // Similar to Best_color_perceptual(), but with floating point { int col; double best_diff=9e99; double target_bri; double bri; double diff_b, diff_c, diff; if (r<0.0) r=0; else if (r>255.0) r=255.0; if (g<0.0) g=0; else if (g>255.0) g=255.0; if (b<0.0) b=0; else if (b>255.0) b=255.0; // Similar to Perceptual_lightness(); target_bri = sqrt(0.26*r*0.26*r + 0.55*g*0.55*g + 0.19*b*0.19*b); for (col=0; col<256; col++) { if (Exclude_color[col]) continue; diff_c = sqrt( (0.26*(Main.palette[col].R-r))* (0.26*(Main.palette[col].R-r))+ (0.55*(Main.palette[col].G-g))* (0.55*(Main.palette[col].G-g))+ (0.19*(Main.palette[col].B-b))* (0.19*(Main.palette[col].B-b))); // Exact match if (diff_c<1.0) { lua_pushinteger(L, col); return 1; } bri = sqrt(0.26*0.26*(Main.palette[col].R*Main.palette[col].R) + 0.55*0.55*(Main.palette[col].G*Main.palette[col].G) + 0.19*0.19*(Main.palette[col].B*Main.palette[col].B)); diff_b = fabs(target_bri-bri); diff=l_weight*(diff_b-diff_c)+diff_c; if (diffPages->Transparent_color); return 1; } int L_SetForeColor(lua_State* L) { byte c; int nb_args=lua_gettop(L); LUA_ARG_LIMIT (1, "setforecolor"); LUA_ARG_NUMBER(1, "setforecolor", c, -DBL_MAX, DBL_MAX); Fore_color = c; return 0; } int L_SetBackColor(lua_State* L) { byte c; int nb_args=lua_gettop(L); LUA_ARG_LIMIT (1, "setbackcolor"); LUA_ARG_NUMBER(1, "setbackcolor", c, -DBL_MAX, DBL_MAX); Back_color = c; return 0; } int L_InputBox(lua_State* L) { #define max_settings (9) #define args_per_setting (5) double min_value[max_settings]; double max_value[max_settings]; double decimal_places[max_settings]; double current_value[max_settings]; const char * label[max_settings]; unsigned short control[max_settings*3+1]; // Each value has at most 3 widgets. enum CONTROL_TYPE { CONTROL_OK = 0x0100, CONTROL_CANCEL = 0x0200, CONTROL_INPUT = 0x0300, CONTROL_INPUT_MINUS = 0x0400, CONTROL_INPUT_PLUS = 0x0500, CONTROL_CHECKBOX = 0x0600, CONTROL_VALUE_MASK = 0x00FF, CONTROL_TYPE_MASK = 0xFF00 }; const char * window_caption; int caption_length; int nb_settings; int nb_args; unsigned int max_label_length; int setting; short clicked_button; char str[40]; short close_window = 0; nb_args = lua_gettop (L); if (nb_args < 6) { return luaL_error(L, "inputbox: Less than 6 arguments"); } if ((nb_args - 1) % args_per_setting) { return luaL_error(L, "inputbox: Wrong number of arguments"); } nb_settings = (nb_args-1)/args_per_setting; if (nb_settings > max_settings) { return luaL_error(L, "inputbox: Too many settings, limit reached"); } max_label_length=4; // Minimum size to account for OK / Cancel buttons // First argument is window caption LUA_ARG_STRING(1, "inputbox", window_caption); caption_length = strlen(window_caption); if ( caption_length > 14) max_label_length = caption_length - 10; for (setting=0; setting 999999999999999.0) max_value[setting] = 999999999999999.0;*/ LUA_ARG_NUMBER(setting*args_per_setting+6, "inputbox", decimal_places[setting], -15.0, 15.0); if (decimal_places[setting]>15) decimal_places[setting]=15; if (min_value[setting]!=0 || max_value[setting]!=1) if (decimal_places[setting]<0) decimal_places[setting]=0; // Keep current value in range if (decimal_places[setting]>=0) current_value[setting] = Fround(current_value[setting], decimal_places[setting]); if (current_value[setting] < min_value[setting]) current_value[setting] = min_value[setting]; else if (current_value[setting] > max_value[setting]) current_value[setting] = max_value[setting]; if (min_value[setting]==0 && max_value[setting]==0) label_length-=12; if (label_length > (int)max_label_length) max_label_length = label_length; } // Max is 25 to keep window under 320 pixel wide if (max_label_length>25) max_label_length=25; Update_colors_during_script(); if (!Cursor_is_visible) Display_cursor(); Open_window(115+max_label_length*8,44+nb_settings*17,window_caption); // Normally this index is unused, but this initialization avoids // any weird behavior if it was used by mistake. control[0]=0; // OK Window_set_normal_button( 7, 25 + 17 * nb_settings, 51,14,"OK" , 0,1,KEY_RETURN); control[Window_nb_buttons] = CONTROL_OK; // Cancel Window_set_normal_button( 64, 25 + 17 * nb_settings, 51,14,"Cancel" , 0,1,KEY_ESC); control[Window_nb_buttons] = CONTROL_CANCEL; for (setting=0; setting0) { setting = control[clicked_button] & (CONTROL_VALUE_MASK); switch (control[clicked_button] & CONTROL_TYPE_MASK) { case CONTROL_OK: close_window = CONTROL_OK; break; case CONTROL_CANCEL: close_window = CONTROL_CANCEL; break; case CONTROL_INPUT: Sprint_double(str,current_value[setting],decimal_places[setting],0); Readline_ex(12+max_label_length*8+23, 22+setting*17,str,7,40,INPUT_TYPE_DECIMAL,decimal_places[setting]); current_value[setting]=atof(str); if (current_value[setting] < min_value[setting]) current_value[setting] = min_value[setting]; else if (current_value[setting] > max_value[setting]) current_value[setting] = max_value[setting]; // Print editable value Sprint_double(str,current_value[setting],decimal_places[setting],7); Print_in_window_limited(12+max_label_length*8+23, 22+setting*17, str, 7,MC_Black,MC_Light); // Display_cursor(); break; case CONTROL_INPUT_MINUS: if (current_value[setting] > min_value[setting]) { current_value[setting]--; if (current_value[setting] < min_value[setting]) current_value[setting] = min_value[setting]; Hide_cursor(); // Print editable value Sprint_double(str,current_value[setting],decimal_places[setting],7); Print_in_window_limited(12+max_label_length*8+23, 22+setting*17, str, 7,MC_Black,MC_Light); // Display_cursor(); } break; case CONTROL_INPUT_PLUS: if (current_value[setting] < max_value[setting]) { current_value[setting]++; if (current_value[setting] > max_value[setting]) current_value[setting] = max_value[setting]; Hide_cursor(); // Print editable value Sprint_double(str,current_value[setting],decimal_places[setting],7); Print_in_window_limited(12+max_label_length*8+23, 22+setting*17, str, 7,MC_Black,MC_Light); // Display_cursor(); } break; case CONTROL_CHECKBOX: if (decimal_places[setting]==0 || current_value[setting]==0.0) { current_value[setting] = (current_value[setting]==0.0); Hide_cursor(); Print_in_window(12+max_label_length*8+46, 22+setting*17, current_value[setting]?"X":" ",MC_Black,MC_Light); // Uncheck other buttons of same family if (decimal_places[setting]<0) { byte button; for (button=3; button<=Window_nb_buttons; button++) { if (button != clicked_button && control[button] & CONTROL_CHECKBOX) { byte other_setting = control[button] & (CONTROL_VALUE_MASK); if (decimal_places[other_setting] == decimal_places[setting]) { // Same family: unset and uncheck current_value[other_setting]=0.0; Print_in_window(12+max_label_length*8+46, 22+other_setting*17, " ",MC_Black,MC_Light); } } } } Display_cursor(); } break; } } } Close_window(); Cursor_shape=CURSOR_SHAPE_HOURGLASS; Display_cursor(); Cursor_is_visible=1; // Return values: // One boolean to tell if user pressed ok or cancel lua_pushboolean(L, (close_window == CONTROL_OK)); // One value per control for (setting=0; setting max_label_length) max_label_length = caption_length; for (button=0; button max_label_length) max_label_length = strlen(label[button]); LUA_ARG_FUNCTION(button*2+3, "selectbox"); } // Max is 25 to keep window under 320 pixel wide if (max_label_length>25) max_label_length=25; Update_colors_during_script(); if (!Cursor_is_visible) Display_cursor(); Open_window(28+max_label_length*8,26+nb_buttons*17,window_caption); for (button=0; button= 3) { LUA_ARG_STRING(3, "windowopen", title); LUA_ARG_LIMIT (3, "windowopen"); } if (Windows_open>=7) { return luaL_error(L, "windowopen: Too many nested windows!"); } if (!Cursor_is_visible) Display_cursor(); Open_window(w,h,title); Cursor_is_visible=0; return 0; } int L_WindowClose(lua_State* L) { int nb_args = lua_gettop(L); LUA_ARG_LIMIT (0, "windowclose"); if (!Windows_open) return luaL_error(L, "windowclose: No window is open"); if (!Cursor_is_visible) Display_cursor(); Close_window(); Cursor_is_visible=0; return 0; } int L_WindowDoDialog(lua_State* L) { int button; int nb_args = lua_gettop(L); LUA_ARG_LIMIT (0, "windowdodialog"); if (!Windows_open) return luaL_error(L, "windowdodialog: No window is open"); //Update_window_area(0,0,Window_width, Window_height); if (!Cursor_is_visible) { Display_cursor(); Cursor_is_visible=1; } if (Window_needs_update) { Update_window_area(0,0,Window_width, Window_height); Window_needs_update=0; } button=Window_clicked_button(); lua_pushinteger(L, button); lua_pushinteger(L, Window_attribute2); lua_pushinteger(L, Key); return 3; } int L_WindowButton(lua_State* L) { int x, y, w, h, key=KEY_NONE; const char *text=""; int nb_args = lua_gettop(L); LUA_ARG_NUMBER(1, "windowbutton", x, 1, Window_width-1); LUA_ARG_NUMBER(2, "windowbutton", y, 1, Window_height-1); LUA_ARG_NUMBER(3, "windowbutton", w, 1, Window_width-1-x); LUA_ARG_NUMBER(4, "windowbutton", h, 1, Window_height-1-y); if (nb_args >= 5) { LUA_ARG_STRING(5, "windowbutton", text); } if (nb_args >= 6) { LUA_ARG_NUMBER(6, "windowbutton", key, -1, INT_MAX); LUA_ARG_LIMIT (6, "windowbutton"); } if (!Windows_open) return luaL_error(L, "windowbutton: No window is open"); if (Cursor_is_visible) { Hide_cursor(); Cursor_is_visible=0; } Window_set_normal_button(x, y, w, h, text, 0, 1, key); Window_needs_update=1; return 0; } int L_WindowRepeatButton(lua_State* L) { int x, y, w, h, key=KEY_NONE; const char *text=""; int nb_args = lua_gettop(L); LUA_ARG_NUMBER(1, "windowrepeatbutton", x, 1, Window_width-1); LUA_ARG_NUMBER(2, "windowrepeatbutton", y, 1, Window_height-1); LUA_ARG_NUMBER(3, "windowrepeatbutton", w, 1, Window_width-1-x); LUA_ARG_NUMBER(4, "windowrepeatbutton", h, 1, Window_height-1-y); if (nb_args >= 5) { LUA_ARG_STRING(5, "windowrepeatbutton", text); } if (nb_args >= 6) { LUA_ARG_NUMBER(6, "windowrepeatbutton", key, -1, INT_MAX); LUA_ARG_LIMIT (6, "windowrepeatbutton"); } if (!Windows_open) return luaL_error(L, "windowrepeatbutton: No window is open"); if (Cursor_is_visible) { Hide_cursor(); Cursor_is_visible=0; } Window_set_repeatable_button(x, y, w, h, text, 0, 1, key); Window_needs_update=1; return 0; } int L_WindowInput(lua_State* L) { int x, y, nbchar; const char *text=NULL; T_Special_button *button; int nb_args = lua_gettop(L); LUA_ARG_NUMBER(1, "windowinput", x, 3, Window_width-3-8); LUA_ARG_NUMBER(2, "windowinput", y, 3, Window_height-3-8); LUA_ARG_NUMBER(3, "windowinput", nbchar, 1, (Window_width-3-8-x)/8); if (nb_args >= 4) { LUA_ARG_STRING(4, "windowinput", text); LUA_ARG_LIMIT (4, "windowinput"); } if (!Windows_open) return luaL_error(L, "windowinput: No window is open"); if (Cursor_is_visible) { Hide_cursor(); Cursor_is_visible=0; } button = Window_set_input_button(x-2, y-2, nbchar); if (text!=NULL) Window_input_content(button, text); Window_needs_update=1; return 0; } int L_WindowReadline(lua_State* L) { int x, y, visible_size, max_size, decimal_places=0; const char *valuetext; double valuenumber; char text[255+1]=""; int result; int input_type=0; // see Readline_ex() int nb_args = lua_gettop(L); LUA_ARG_NUMBER(1, "windowreadline", x, 3, Window_width-3-8); LUA_ARG_NUMBER(2, "windowreadline", y, 3, Window_height-3-8); // arg 3 can be either string or number, it's checked below if (nb_args < (3)) return luaL_error(L, "%s: Argument %d is missing.", "windowreadline", (3)); LUA_ARG_NUMBER(4, "windowreadline", visible_size, 1, (Window_height-3-8)/8); LUA_ARG_NUMBER(5, "windowreadline", max_size, 1, 255); LUA_ARG_NUMBER(6, "windowreadline", decimal_places, 0, 16); LUA_ARG_NUMBER(7, "windowreadline", input_type, 0, 4); LUA_ARG_LIMIT (7, "windowreadline"); if (input_type == 3 || input_type == 1) { // expect number LUA_ARG_NUMBER(3, "windowreadline", valuenumber, -DBL_MAX, DBL_MAX); Sprint_double(text, valuenumber, decimal_places, 0); } else { // expect string LUA_ARG_STRING(3, "windowreadline", valuetext); if (strlen(valuetext)>255) return luaL_error(L, "%s: Argument %d, string too long.", "windowreadline", (3)); strcpy(text, valuetext); } if (!Windows_open) return luaL_error(L, "windowreadline: No window is open"); if (Cursor_is_visible) { Hide_cursor(); Cursor_is_visible=0; } result = Readline_ex(x, y, text, visible_size, max_size, input_type, decimal_places); Window_needs_update=1; lua_pushboolean(L, result); // 0=ESC, 1= confirm lua_pushstring(L, text); return 2; } int L_WindowPrint(lua_State* L) { int x, y, fg=0, bg=2; int colors[4] = {MC_Black, MC_Dark, MC_Light, MC_White}; const char *text=""; int nb_args = lua_gettop(L); LUA_ARG_NUMBER(1, "windowprint", x, 1, Window_width-8); LUA_ARG_NUMBER(2, "windowprint", y, 1, Window_height-8); LUA_ARG_STRING(3, "windowprint", text); if (nb_args >= 4) { LUA_ARG_NUMBER(4, "windowprint", fg, 0, 3); } if (nb_args >= 5) { LUA_ARG_NUMBER(5, "windowprint", bg, 0, 3); LUA_ARG_LIMIT (5, "windowprint"); } if (!Windows_open) return luaL_error(L, "windowprint: No window is open"); if (Cursor_is_visible) { Hide_cursor(); Cursor_is_visible=0; } Print_in_window_limited(x, y, text, (Window_width-x)/8,colors[fg], colors[bg]); Window_needs_update=1; return 0; } int L_WindowSlider(lua_State* L) { word x, y, height, nb_elements, nb_elements_visible, initial_position; int horizontal; int nb_args = lua_gettop(L); LUA_ARG_LIMIT (7, "windowslider"); LUA_ARG_BOOL (7, "windowslider", horizontal); if (horizontal) { LUA_ARG_NUMBER(1, "windowslider", x, 1, Window_width-27); LUA_ARG_NUMBER(2, "windowslider", y, 1, Window_height-13); LUA_ARG_NUMBER(3, "windowslider", height, 1, Window_width-27-x); } else { LUA_ARG_NUMBER(1, "windowslider", x, 1, Window_width-13); LUA_ARG_NUMBER(2, "windowslider", y, 1, Window_height-27); LUA_ARG_NUMBER(3, "windowslider", height, 1, Window_height-27-y); } LUA_ARG_NUMBER(4, "windowslider", nb_elements, 1, INT_MAX); LUA_ARG_NUMBER(5, "windowslider", nb_elements_visible, 1, nb_elements); LUA_ARG_NUMBER(6, "windowslider", initial_position, 0, nb_elements-nb_elements_visible); if (!Windows_open) return luaL_error(L, "windowslider: No window is open"); if (Cursor_is_visible) { Hide_cursor(); Cursor_is_visible=0; } if (horizontal) Window_set_horizontal_scroller_button(x, y, height+24, nb_elements, nb_elements_visible, initial_position); else Window_set_scroller_button(x, y, height+24, nb_elements, nb_elements_visible, initial_position); Window_needs_update=1; return 0; } int L_WindowMoveSlider(lua_State* L) { word nb_elements, nb_elements_visible, position; int slider_number; T_Scroller_button *button = Window_scroller_button_list; int nb_args = lua_gettop(L); if (!Windows_open) return luaL_error(L, "windowmoveslider: No window is open"); LUA_ARG_NUMBER(1, "windowmoveslider", slider_number, 1, 256); button = Window_scroller_button_list; if (! button) return luaL_error(L, "windowmoveslider: No slider to move"); while (slider_number>1) { slider_number--; button = button->Next; if (! button) return luaL_error(L, "windowmoveslider: Not so many sliders in this window"); } LUA_ARG_NUMBER(2, "windowmoveslider", nb_elements, 1, INT_MAX); LUA_ARG_NUMBER(3, "windowmoveslider", nb_elements_visible, 1, nb_elements); LUA_ARG_NUMBER(4, "windowmoveslider", position, 0, nb_elements-nb_elements_visible); LUA_ARG_LIMIT (4, "windowmoveslider"); if (Cursor_is_visible) { Hide_cursor(); Cursor_is_visible=0; } button->Nb_elements =nb_elements; button->Nb_visibles =nb_elements_visible; button->Position =position; Compute_slider_cursor_length(button); Window_draw_slider(button); Window_needs_update=1; return 0; } int L_UpdateScreen(lua_State* L) { int nb_args=lua_gettop(L); LUA_ARG_LIMIT (0, "updatescreen"); Update_colors_during_script(); if (Cursor_is_visible) Hide_cursor(); Display_all_screen(); Display_cursor(); Cursor_is_visible=1; //Flush_update(); return 0; } int L_StatusMessage(lua_State* L) { const char* msg; char msg2[25]; int len; int nb_args = lua_gettop(L); LUA_ARG_LIMIT(1,"statusmessage"); LUA_ARG_STRING(1, "statusmessage", msg); len=strlen(msg); if (len<=24) { strcpy(msg2, msg); // fill remainder with spaces for (;len<24;len++) msg2[len]=' '; } else { strncpy(msg2, msg, 24); } msg2[24]='\0'; if (Cursor_is_visible) Hide_cursor(); Print_in_menu(msg2,0); Cursor_is_visible=0; return 0; } int L_GetLayerCount(lua_State* L) { int nb_args=lua_gettop(L); LUA_ARG_LIMIT (0, "layercount"); lua_pushinteger(L, Main.backups->Pages->Nb_layers); return 1; } int L_GetSpareLayerCount(lua_State* L) { int nb_args=lua_gettop(L); LUA_ARG_LIMIT (0, "layercount"); lua_pushinteger(L, Spare.backups->Pages->Nb_layers); return 1; } int L_SelectSpareLayer(lua_State* L) { int nb_args=lua_gettop(L); LUA_ARG_LIMIT (1, "selectsparelayer"); LUA_ARG_NUMBER(1, "selectsparelayer", Spare.current_layer, 0, Spare.backups->Pages->Nb_layers - 1); if (Spare.backups->Pages->Image_mode != IMAGE_MODE_ANIMATION) { if (! ((1 << Spare.current_layer) & Spare.layers_visible)) { Spare.layers_visible |= (1 << Spare.current_layer); } } return 0; } int L_SelectLayer(lua_State* L) { int nb_args=lua_gettop(L); LUA_ARG_LIMIT (1, "selectlayer"); LUA_ARG_NUMBER(1, "selectlayer", Main.current_layer, 0, Main.backups->Pages->Nb_layers - 1); Backup_if_necessary(L, Main.current_layer); // if (Main.backups->Pages->Image_mode != IMAGE_MODE_ANIMATION) { if (! ((1 << Main.current_layer) & Main.layers_visible)) { Main.layers_visible |= (1 << Main.current_layer); Redraw_layered_image(); } else { Update_depth_buffer(); // Only need the depth buffer } } return 0; } int L_FinalizePicture(lua_State* L) { int nb_args=lua_gettop(L); LUA_ARG_LIMIT (0, "finalizepicture"); Update_colors_during_script(); if (Is_backed_up) { End_of_modification(); } Is_backed_up = 0; Main_backup_page = Main.backups->Pages; Main_backup_screen = Main_screen; Register_main_readonly(L); lua_register(L,"setcolor",L_SetColor_unsaved); return 0; } int L_GetFileName(lua_State* L) { int nb_args=lua_gettop(L); LUA_ARG_LIMIT (0, "getfilename"); #if defined(WIN32) if (Main.backups->Pages->Filename_unicode == NULL || Main.backups->Pages->Filename_unicode[0] == 0) lua_pushstring(L, Main.backups->Pages->Filename); else { // convert Filename_unicode to UTF8 (Lua strings are 8bits) size_t len = 0; char * utf8name = Unicode_to_utf8(Main.backups->Pages->Filename_unicode, &len); if (utf8name == NULL) { lua_pushstring(L, Main.backups->Pages->Filename); } else { lua_pushlstring(L, utf8name, len); free(utf8name); } } // still using the "short name" directory path at the moment lua_pushstring(L, Main.backups->Pages->File_directory); #else lua_pushstring(L, Main.backups->Pages->Filename); lua_pushstring(L, Main.backups->Pages->File_directory); #endif return 2; } /// Run a script while changing the current directory int L_Run(lua_State* L) { const char * script_arg; const char * message; char * saved_directory; char * full_path; const char * path; char * file_name; static int nested_calls = 0; int nb_args = lua_gettop(L); LUA_ARG_LIMIT (1, "run"); LUA_ARG_STRING(1, "run", script_arg); nested_calls++; if (nested_calls > 100) return luaL_error(L, "run: too many nested calls (100)"); // store the current directory (on the stack) saved_directory = Get_current_directory(NULL, NULL, 0); full_path = strdup(script_arg); #if defined (__AROS__) // Convert path written on Linux/Windows norms to AROS norms : // Each element like ../ and ..\ is replaced by / // Each path separator \ is replaced by / { const char *src = script_arg; char *dst = full_path; char c; do { while (src[0]=='.' && src[1]=='.' && (src[2]=='/' || src[2]=='\\')) { *dst='/'; dst++; src+=3; } // copy until / or \ or NUL included do { c=*src; *dst=c=='\\' ? '/' : c; // Replace \ by / anyway src++; dst++; } while (c != '\0' && c != '/' && c != '\\'); } while (c!='\0'); } #else { // Convert any number of '\' in the path to '/' // It is not useful on Windows, but does no harm (both path separators work) // On Linux/Unix, this ensures that scripts written and tested on Windows // work similarly. char *pos; for (pos = strchr(full_path, '\\'); pos != NULL; pos = strchr(pos, '\\')) *pos = '/'; } #endif file_name = Find_last_separator(full_path); if (file_name != NULL) { path = full_path; *file_name = '\0'; file_name++; if (path[0] == '\0') // the file was in ROOT directory path = PATH_SEPARATOR; if (!Directory_exists(path)) { free(saved_directory); free(full_path); return luaL_error(L, "run: directory of script doesn't exist"); } Change_directory(path); } else { path = NULL; // only file name file_name = full_path; } if (luaL_loadfile(L, file_name) != 0) { int r; nb_args = lua_gettop(L); if (nb_args>0 && (message = lua_tostring(L, nb_args))!=NULL) r = luaL_error(L, message); else r = luaL_error(L, "run: Unknown error loading script %s", file_name); Change_directory(saved_directory); free(saved_directory); free(full_path); return r; } free(full_path); if (lua_pcall(L, 0, 0, 0) != 0) { int r; nb_args = lua_gettop(L); // We COULD build the call stack, but I think this would be more // confusing than helpful. if (nb_args>0 && (message = lua_tostring(L, nb_args))!=NULL) r = luaL_error(L, message); else r = luaL_error(L, "run: Unknown error running script!"); Change_directory(saved_directory); free(saved_directory); return r; } nested_calls--; // restore directory Change_directory(saved_directory); free(saved_directory); return 0; } // "unsaved" version of the bindings DECLARE_UNSAVED(L_ClearPicture) DECLARE_UNSAVED(L_DrawCircle) DECLARE_UNSAVED(L_DrawDisk) DECLARE_UNSAVED(L_DrawFilledRect) DECLARE_UNSAVED(L_DrawLine) DECLARE_UNSAVED(L_PutPicturePixel) /// Bindings for screen-drawing Lua functions, if the current image is backed up. void Register_main_writable(lua_State* L) { lua_register(L,"putpicturepixel",L_PutPicturePixel); lua_register(L,"drawline",L_DrawLine); lua_register(L,"drawfilledrect",L_DrawFilledRect); lua_register(L,"drawcircle",L_DrawCircle); lua_register(L,"drawdisk",L_DrawDisk); lua_register(L,"clearpicture",L_ClearPicture); } /// Bindings for screen-drawing Lua functions, if the current image is not backed up yet. void Register_main_readonly(lua_State* L) { lua_register(L,"putpicturepixel",L_PutPicturePixel_unsaved); lua_register(L,"drawline",L_DrawLine_unsaved); lua_register(L,"drawfilledrect",L_DrawFilledRect_unsaved); lua_register(L,"drawcircle",L_DrawCircle_unsaved); lua_register(L,"drawdisk",L_DrawDisk_unsaved); lua_register(L,"clearpicture",L_ClearPicture_unsaved); } // Handlers for window internals T_Fileselector Scripts_selector; // Callback to display a skin name in the list void Draw_script_name(word x, word y, word index, byte highlighted) { T_Fileselector_item * current_item; if (Scripts_selector.Nb_elements) { byte fg, bg; current_item = Get_item_by_index(&Scripts_selector, index); if (current_item->Type==0) // Files { fg=(highlighted)?MC_White:MC_Light; bg=(highlighted)?MC_Dark:MC_Black; } else if (current_item->Type==1) // Directories { fg=(highlighted)?MC_Light:MC_Dark; bg=(highlighted)?MC_Dark:MC_Black; } else // Drives { fg=(highlighted)?MC_Light:MC_Dark; bg=(highlighted)?MC_Dark:MC_Black; if (current_item->Icon != ICON_NONE) { Window_display_icon_sprite(x,y,current_item->Icon); x+=8; } } if (current_item->Unicode_short_name != NULL) Print_in_window_unicode(x, y, current_item->Unicode_short_name, fg, bg); else Print_in_window(x, y, current_item->Short_name, fg,bg); Update_window_area(x,y,NAME_WIDTH*8,8); } } /// /// Displays first lines of comments from a lua script in the window. void Draw_script_information(T_Fileselector_item * script_item, const char *full_directory) { FILE *script_file; char * full_name; char text_block[3][DESC_WIDTH+1]; int x, y; int i; // Blank the target area Window_rectangle(7, FILESEL_Y + 89, DESC_WIDTH*6+2, 4*8, MC_Black); if (script_item && script_item->Full_name && script_item->Full_name[0] != '\0') { full_name = Filepath_append_to_dir(full_directory, script_item->Full_name); x = 0; y = 0; text_block[0][0] = text_block[1][0] = text_block[2][0] = '\0'; if (script_item->Type == FSOBJECT_FILE) { // Start reading script_file = fopen(full_name, "r"); if (script_file != NULL) { int c; c = fgetc(script_file); while (c != EOF && y<3) { if (c == '\n') { if (x<2) break; // Carriage return without comment: Stopping y++; x=0; } else if (x==0 || x==1) { if (c != '-') break; // Non-comment line was encountered. Stopping. x++; } else { if (x < DESC_WIDTH+2) { // Adding character text_block[y][x-2] = (c<32 || c>255) ? ' ' : c; text_block[y][x-1] = '\0'; } x++; } // Read next c = fgetc(script_file); } fclose(script_file); } Print_help(8, FILESEL_Y + 89 , text_block[0], 'N', 0, 0); Print_help(8, FILESEL_Y + 89+ 8, text_block[1], 'N', 0, 0); Print_help(8, FILESEL_Y + 89+16, text_block[2], 'N', 0, 0); // Display a line with the keyboard shortcut Print_help(8, FILESEL_Y + 89+24, "Key:", 'N', 0, 0); for (i=0; i<10; i++) if (Bound_script[i]!=NULL && !strcmp(Bound_script[i], full_name)) break; if (i<10) { const char *shortcut; shortcut = Keyboard_shortcut_value(SPECIAL_RUN_SCRIPT_1+i); Print_help(8+4*6, FILESEL_Y + 89+24, shortcut, 'K', 0, strlen(shortcut)); } else { Print_help(8+4*6, FILESEL_Y + 89+24, "None", 'K', 0, 4); } } free(full_name); } Update_window_area(8, FILESEL_Y + 89, DESC_WIDTH*6+2, 4*8); } // Add a script to the list static void Add_script(void * pdata, const char *file_name, const word *unicode_name, byte is_file, byte is_directory, byte is_hidden) { int len; T_Fileselector_item * item; (void)pdata; if (is_file) { // Only files ending in ".lua" len=strlen(file_name); if (len<=4 || strcasecmp(file_name+len-4, ".lua")) return; // Hidden if (is_hidden && !Config.Show_hidden_files) return; item = Add_element_to_list(&Scripts_selector, file_name, Format_filename(file_name, NAME_WIDTH+1, 0), FSOBJECT_FILE, ICON_NONE); if (item != NULL && unicode_name != NULL) { item->Unicode_full_name = Unicode_strdup(unicode_name); item->Unicode_short_name = Unicode_strdup(Format_filename_unicode(unicode_name, NAME_WIDTH+1, 1)); } } else if (is_directory) { // Ignore current directory if ( !strcmp(file_name, ".")) return; // Ignore parent directory entry if (!strcmp(file_name, PARENT_DIR)) return; // Hidden if (is_hidden && !Config.Show_hidden_directories) return; item = Add_element_to_list(&Scripts_selector, file_name, Format_filename(file_name, NAME_WIDTH+1, 1), FSOBJECT_DIR, ICON_NONE); if (item != NULL && unicode_name != NULL) { item->Unicode_full_name = Unicode_strdup(unicode_name); item->Unicode_short_name = Unicode_strdup(Format_filename_unicode(unicode_name, NAME_WIDTH+1, 1)); } } } void Highlight_script(T_Fileselector *selector, T_List_button *list, const char *selected_file) { short index; index=Find_file_in_fileselector(selector, selected_file); if (index < 0) index = 0; // 1st item if selected_file is not found Locate_list_item(list, index); } static char * Last_run_script = NULL; // Before: Cursor hidden // After: Cursor shown void Run_script(const char *script_subdirectory, const char *script_filename) { lua_State* L; const char* message; byte old_cursor_shape = Cursor_shape; char * path; int original_image_width = Main.image_width; int original_image_height = Main.image_height; int original_current_layer = Main.current_layer; // Some scripts are slow Cursor_shape = CURSOR_SHAPE_HOURGLASS; Display_cursor(); Flush_update(); Cursor_is_visible=1; free(Last_run_script); if (script_subdirectory && script_subdirectory[0]!='\0') Last_run_script = Filepath_append_to_dir(script_subdirectory, script_filename); else Last_run_script = strdup(script_filename); // This chdir is for the script's sake. Grafx2 itself will (try to) // not rely on what is the system's current directory. path = Extract_path(NULL, Last_run_script); Change_directory(path); free(path); L = luaL_newstate(); // used to be lua_open() on Lua 5.1, deprecated on 5.2 /// @todo as the value doesn't vary, this should be /// done once at the start of the program path = GFX2_malloc(strlen(Data_directory) + strlen(SCRIPTS_SUBDIRECTORY) + strlen(LUALIB_SUBDIRECTORY) + 5 + 3 * strlen(PATH_SEPARATOR) + 9 + 1); if (path == NULL) return; strcpy(path, Data_directory); Append_path(path, SCRIPTS_SUBDIRECTORY, NULL); Append_path(path, LUALIB_SUBDIRECTORY, NULL); Append_path(path, "?.lua", NULL); // SetEnvironmentVariableA() won't work because lua uses getenv() #if defined(_MSC_VER) if (_putenv_s("LUA_PATH", path) < 0) GFX2_Log(GFX2_ERROR, "_putenv_s(\"LUA_PATH\", \"%s\") failed\n", path); #elif defined(WIN32) // Mingw has neither setenv() nor _putenv_s() memmove(path + 9, path, strlen(path) + 1); memcpy(path, "LUA_PATH=", 9); if (putenv(path) < 0) GFX2_Log(GFX2_ERROR, "putenv(\"%s\") failed\n", path); #else /* From linux man : * This function makes * copies of the strings pointed to by name and value (by contrast with * putenv(3)). */ if (setenv("LUA_PATH", path, 1) < 0) GFX2_Log(GFX2_ERROR, "setenv(\"LUA_PATH\", \"%s\", 1) failed\n", path); #endif free(path); // Drawing lua_register(L,"putbrushpixel",L_PutBrushPixel); lua_register(L,"putsparepicturepixel",L_PutSparePicturePixel); Register_main_readonly(L); // Reading pixels lua_register(L,"getbrushpixel",L_GetBrushPixel); lua_register(L,"getbrushbackuppixel",L_GetBrushBackupPixel); lua_register(L,"getpicturepixel",L_GetPicturePixel); lua_register(L,"getlayerpixel",L_GetLayerPixel); lua_register(L,"getbackuppixel",L_GetBackupPixel); lua_register(L,"getsparelayerpixel",L_GetSpareLayerPixel); lua_register(L,"getsparepicturepixel",L_GetSparePicturePixel); // Sizes lua_register(L,"setbrushsize",L_SetBrushSize); lua_register(L,"setpicturesize",L_SetPictureSize); lua_register(L,"setsparepicturesize",L_SetSparePictureSize); lua_register(L,"getbrushsize",L_GetBrushSize); lua_register(L,"getpicturesize",L_GetPictureSize); lua_register(L,"getsparepicturesize",L_GetSparePictureSize); // color and palette lua_register(L,"getforecolor",L_GetForeColor); lua_register(L,"getbackcolor",L_GetBackColor); lua_register(L,"gettranscolor",L_GetTransColor); lua_register(L,"setcolor",L_SetColor_unsaved); lua_register(L,"setforecolor",L_SetForeColor); lua_register(L,"setbackcolor",L_SetBackColor); lua_register(L,"getcolor",L_GetColor); lua_register(L,"getbackupcolor",L_GetBackupColor); lua_register(L,"getsparecolor",L_GetSpareColor); lua_register(L,"getsparetranscolor",L_GetSpareTransColor); lua_register(L,"matchcolor",L_MatchColor); lua_register(L,"matchcolor2",L_MatchColor2); // layers lua_register(L,"selectlayer",L_SelectLayer); lua_register(L,"selectsparelayer",L_SelectSpareLayer); lua_register(L,"getlayercount",L_GetLayerCount); lua_register(L,"getsparelayercount",L_GetSpareLayerCount); // ui lua_register(L,"inputbox",L_InputBox); lua_register(L,"messagebox",L_MessageBox); lua_register(L,"statusmessage",L_StatusMessage); lua_register(L,"selectbox",L_SelectBox); // misc. stuff lua_register(L,"wait",L_Wait); lua_register(L,"waitbreak",L_WaitBreak); lua_register(L,"waitinput",L_WaitInput); lua_register(L,"updatescreen",L_UpdateScreen); lua_register(L,"finalizepicture",L_FinalizePicture); lua_register(L,"getfilename",L_GetFileName); lua_register(L,"run",L_Run); // dialog lua_register(L,"windowopen",L_WindowOpen); lua_register(L,"windowclose",L_WindowClose); lua_register(L,"windowdodialog",L_WindowDoDialog); lua_register(L,"windowbutton",L_WindowButton); lua_register(L,"windowrepeatbutton",L_WindowRepeatButton); lua_register(L,"windowinput",L_WindowInput); lua_register(L,"windowreadline",L_WindowReadline); lua_register(L,"windowprint",L_WindowPrint); lua_register(L,"windowslider",L_WindowSlider); lua_register(L,"windowmoveslider",L_WindowMoveSlider); // Load all standard libraries luaL_openlibs(L); /* luaopen_base(L); //luaopen_package(L); // crashes on Windows, for unknown reason luaopen_table(L); //luaopen_io(L); // crashes on Windows, for unknown reason //luaopen_os(L); luaopen_string(L); luaopen_math(L); //luaopen_debug(L); */ // TODO The script may modify the picture, so we do a backup here. // If the script is only touching the brush, this isn't needed... // The backup also allows the script to read from it to make something // like a feedback off effect (convolution matrix comes to mind). //Backup(); Is_backed_up = 0; Main_backup_page = Main.backups->Pages; Main_backup_screen = Main_screen; Backup_the_spare(LAYER_ALL); Palette_has_changed=0; Brush_was_altered=0; Original_back_color=Back_color; Original_fore_color=Fore_color; // Backup the brush Brush_backup=(byte *)GFX2_malloc(((long)Brush_height)*Brush_width); Brush_backup_width = Brush_width; Brush_backup_height = Brush_height; if (Brush_backup == NULL) { Verbose_message("Error!", "Out of memory!"); } else { memcpy(Brush_backup, Brush, ((long)Brush_height)*Brush_width); if (luaL_loadfile(L, Last_run_script) != 0) { int stack_size; stack_size= lua_gettop(L); if (stack_size>0 && (message = lua_tostring(L, stack_size))!=NULL) Verbose_message("Error!", message); else Warning_message("Unknown error loading script!"); } else if (lua_pcall(L, 0, 0, 0) != 0) { int stack_size; stack_size= lua_gettop(L); Update_colors_during_script(); if (stack_size>0 && (message = lua_tostring(L, stack_size))!=NULL) Verbose_message("Error running script", message); else Warning_message("Unknown error running script!"); } // Clean up any remaining dialog windows while (Windows_open) { Close_window(); Display_cursor(); } } // Cleanup free(Brush_backup); Brush_backup=NULL; Update_colors_during_script(); if (Is_backed_up) End_of_modification(); Print_in_menu(" ",0); lua_close(L); if (Brush_was_altered) { // Copy Brush to original memcpy(Brush_original_pixels, Brush, (long)Brush_width*Brush_height); Change_paintbrush_shape(PAINTBRUSH_SHAPE_COLOR_BRUSH); } Hide_cursor(); Display_all_screen(); if (original_current_layer != Main.current_layer) Display_layerbar(); // current layer has changed, buttons need to be updated // Update tilemap if image size has changed if (original_image_width!=Main.image_width || original_image_height!=Main.image_height) { Tilemap_update(); } // Update changed pen colors. if (Back_color!=Original_back_color || Fore_color!=Original_fore_color) { // This is done at end of script, in case somebody would use the // functions in a custom window. if (Back_color!=Original_back_color) { byte new_color = Back_color; Back_color = Original_back_color; Set_back_color(new_color); } if (Fore_color!=Original_fore_color) { byte new_color = Fore_color; Fore_color = Original_fore_color; Set_fore_color(new_color); } } Cursor_shape=old_cursor_shape; Display_cursor(); } void Run_numbered_script(byte index) { if (index>=10) return; if (Bound_script[index]==NULL) return; Hide_cursor(); Run_script(NULL, Bound_script[index]); } void Repeat_script(void) { if (Last_run_script==NULL || Last_run_script[0]=='\0') { Warning_message("No script to repeat."); return; } Hide_cursor(); Run_script(NULL, Last_run_script); } void Set_script_shortcut(T_Fileselector_item * script_item, const char *full_directory) { int i; char * full_name; if (script_item && script_item->Full_name && script_item->Full_name[0] != '\0') { full_name = Filepath_append_to_dir(full_directory, script_item->Full_name); // Find if it already has a shortcut for (i=0; i<10; i++) if (Bound_script[i] != NULL && !strcmp(Bound_script[i], full_name)) break; if (i<10) { // Existing shortcut free(full_name); } else { // Try to find a "free" one. for (i=0; i<10; i++) if (Bound_script[i]==NULL || !Has_shortcut(SPECIAL_RUN_SCRIPT_1+i) || !File_exists(Bound_script[i])) break; if (i<10) { free(Bound_script[i]); Bound_script[i] = full_name; } else { Warning_message("Already 10 scripts have shortcuts."); free(full_name); return; } } Window_set_shortcut(SPECIAL_RUN_SCRIPT_1+i); if (!Has_shortcut(SPECIAL_RUN_SCRIPT_1+i)) { // User cancelled or deleted all shortcuts free(Bound_script[i]); Bound_script[i]=NULL; } // Refresh display Hide_cursor(); Draw_script_information(script_item, full_directory); Display_cursor(); } } void Reload_scripts_list(void) { // Reinitialize the list Free_fileselector_list(&Scripts_selector); if (Config.Scripts_directory == NULL || Config.Scripts_directory[0]=='\0') { Read_list_of_drives(&Scripts_selector,NAME_WIDTH+1); } else { Add_element_to_list(&Scripts_selector, PARENT_DIR, Format_filename(PARENT_DIR, NAME_WIDTH+1, 1), FSOBJECT_DIR, ICON_NONE); // Add each found file to the list For_each_directory_entry(Config.Scripts_directory, NULL, Add_script); } // Sort it Sort_list_of_files(&Scripts_selector); // } void Button_Brush_Factory(void) { static char * selected_file = NULL; // currently selected file in factory file selector short clicked_button; T_List_button* scriptlist; T_Scroller_button* scriptscroll; T_Special_button* scriptarea; T_Fileselector_item *item; int last_selected_item=-1; char displayed_path[DESC_WIDTH+1]; int q; Reload_scripts_list(); Open_window(33+8*NAME_WIDTH, 180, "Brush Factory"); Window_set_normal_button(85, 161, 67, 14, "Cancel", 0, 1, KEY_ESC); // 1 Window_display_frame_in(6, FILESEL_Y - 2, NAME_WIDTH*8+4, 84); // File selector // Fileselector scriptarea=Window_set_special_button(8, FILESEL_Y + 0, NAME_WIDTH*8, 80,0); // 2 // Scroller for the fileselector scriptscroll = Window_set_scroller_button(NAME_WIDTH*8+14, FILESEL_Y - 1, 82, Scripts_selector.Nb_elements,10, 0); // 3 scriptlist = Window_set_list_button(scriptarea,scriptscroll,Draw_script_name, 0); // 4 Window_set_normal_button(10, 161, 67, 14, "Run", 0, 1, KEY_RETURN); // 5 Window_display_frame_in(6, FILESEL_Y + 88, DESC_WIDTH*6+4, 4*8+2); // Descr. Window_set_special_button(7, FILESEL_Y + 89+24,DESC_WIDTH*6,8,0); // 6 // Box around path (slightly expands up left) Window_rectangle(8, FILESEL_Y - 13, DESC_WIDTH*6+2, 9, MC_Black); while (1) { // Locate selected file in view Highlight_script(&Scripts_selector, scriptlist, selected_file); // Update the scroller position scriptscroll->Position=scriptlist->List_start; Window_draw_slider(scriptscroll); Window_redraw_list(scriptlist); // Display current path: q = Config.Scripts_directory == NULL ? 0 : strlen(Config.Scripts_directory); if (q<=DESC_WIDTH) { if (q > 0) strcpy(displayed_path, Config.Scripts_directory); for (; q < DESC_WIDTH; q++) displayed_path[q]=' '; displayed_path[q]='\0'; } else { strcpy(displayed_path, Config.Scripts_directory+q-DESC_WIDTH); displayed_path[0] = ELLIPSIS_CHARACTER; } Print_help(9, FILESEL_Y - 12, displayed_path, 'N', 0, 0); Draw_script_information(Get_item_by_index(&Scripts_selector, scriptlist->List_start + scriptlist->Cursor_position), Config.Scripts_directory); Update_window_area(0, 0, Window_width, Window_height); Display_cursor(); // Wait for mouse release (needed for example after a double-click // that navigates to a subdirectory) while (last_selected_item==-1 && Mouse_K) { Get_input(20); } Reset_quicksearch(); do { clicked_button = Window_clicked_button(); if (Key==KEY_BACKSPACE && Config.Scripts_directory != NULL && Config.Scripts_directory[0]!='\0') { // Make it select first entry (parent directory) scriptlist->List_start=0; scriptlist->Cursor_position=0; clicked_button=5; } if (Is_shortcut(Key,0x100+BUTTON_HELP)) Window_help(BUTTON_BRUSH_EFFECTS, "BRUSH FACTORY"); else if (Is_shortcut(Key,0x200+BUTTON_BRUSH_EFFECTS)) clicked_button=1; // Cancel // Quicksearch if (clicked_button==4) Reset_quicksearch(); else if (clicked_button==0 && Key_ANSI) clicked_button=Quicksearch_list(scriptlist, &Scripts_selector); switch (clicked_button) { case 2: // Double-click an entry in script list clicked_button=5; break; case 4: // Select script last_selected_item = scriptlist->List_start + scriptlist->Cursor_position; Hide_cursor(); Draw_script_information(Get_item_by_index(&Scripts_selector, scriptlist->List_start + scriptlist->Cursor_position), Config.Scripts_directory); Display_cursor(); break; case 6: Set_script_shortcut(Get_item_by_index(&Scripts_selector, scriptlist->List_start + scriptlist->Cursor_position), Config.Scripts_directory); break; default: break; } } while (clicked_button != 1 && clicked_button != 5 && !Quit_is_required); if (Quit_is_required) clicked_button = 1; // Cancel if (clicked_button==1) break; // OK if (Scripts_selector.Nb_elements == 0) { // No items : same as Cancel clicked_button=1; break; } // Examine selected file item = Get_item_by_index(&Scripts_selector, scriptlist->List_start + scriptlist->Cursor_position); if (item->Type == FSOBJECT_FILE) { free(selected_file); selected_file = strdup(item->Full_name); break; } else if (item->Type == FSOBJECT_DIR || item->Type == FSOBJECT_DRIVE) { if (item->Type == FSOBJECT_DRIVE) { // Selecting one drive root free(selected_file); selected_file = strdup(PARENT_DIR); free(Config.Scripts_directory); Config.Scripts_directory = strdup(item->Full_name); } else { // Going down one or up by one directory if (strcmp(item->Full_name, PARENT_DIR) == 0) { char * separator_pos = Find_last_separator(Config.Scripts_directory); if (separator_pos != NULL && separator_pos[1] == '\0') { // remove trailing separator separator_pos[0] = '\0'; separator_pos = Find_last_separator(Config.Scripts_directory); } free(selected_file); if (separator_pos == NULL) { selected_file = Config.Scripts_directory; // steal heap buffer Config.Scripts_directory = NULL; } else { selected_file = strdup(separator_pos + 1); separator_pos[0] = '\0'; } } else { char * new_dir = Filepath_append_to_dir(Config.Scripts_directory, item->Full_name); if (new_dir != NULL) { free(Config.Scripts_directory); Config.Scripts_directory = new_dir; } } } // No break: going back up to beginning of loop Reload_scripts_list(); scriptlist->Scroller->Nb_elements=Scripts_selector.Nb_elements; Compute_slider_cursor_length(scriptlist->Scroller); last_selected_item = -1; Hide_cursor(); } } Close_window(); Unselect_button(BUTTON_BRUSH_EFFECTS); if (clicked_button == 5 && selected_file != NULL) // Run the script { Run_script(Config.Scripts_directory, selected_file); } else { Display_cursor(); } Free_fileselector_list(&Scripts_selector); } #else // NOLUA void Button_Brush_Factory(void) { Verbose_message("Error!", "The brush factory is not available in this build of GrafX2."); } /// /// Returns a string stating the included Lua engine version, /// or "Disabled" if Grafx2 is compiled without Lua. const char * Lua_version(void) { return "Disabled"; } #endif