diff --git a/bugs/bug379.rb b/bugs/bug377.rb similarity index 75% rename from bugs/bug379.rb rename to bugs/bug377.rb index e702055d..0016c7b2 100644 --- a/bugs/bug379.rb +++ b/bugs/bug377.rb @@ -3,8 +3,8 @@ require 'shoes/image' stack do flow do - @el = edit_line "#{DIR}/static/shoes-icon-walkabout.png" - #@el = edit_line "https://shoes.mvmanila.com/public/images/dino.jpg" + #@el = edit_line "#{DIR}/static/shoes-icon-walkabout.png" + @el = edit_line "https://shoes.mvmanila.com/public/images/dino.jpg" @cb = check; para "Don't cache" button "(Re)load" do @img.clear @@ -20,18 +20,22 @@ @cview.clear @cview.append do eb = edit_box width: 400 + DATABASE.open if DATABASE.closed? DATABASE.each do |key, value| eb.append "#{key} -> #{value}" end end end button "clear caches" do + DATABASE.open if DATABASE.closed? DATABASE.each do |k, val| v = val.split('|') path = Shoes::image_cache_path v[1], File.extname(k) + puts "delete: #{path}" File.delete path if File.exist? path end DATABASE.clear + DATABASE.close end end @img = flow {} diff --git a/shoes/http.h b/shoes/http.h index 8e718ef2..dfa4d5ce 100644 --- a/shoes/http.h +++ b/shoes/http.h @@ -30,6 +30,7 @@ typedef struct { void shoes_download(shoes_http_request *req); void shoes_queue_download(shoes_http_request *req); +shoes_cached_image* shoes_no_cache_queue_download(shoes_http_request *req); VALUE shoes_http_err(SHOES_DOWNLOAD_ERROR error); SHOES_DOWNLOAD_HEADERS shoes_http_headers(VALUE hsh); void shoes_http_request_free(shoes_http_request *); diff --git a/shoes/http/rbload.c b/shoes/http/rbload.c index 73428b80..24b736f2 100644 --- a/shoes/http/rbload.c +++ b/shoes/http/rbload.c @@ -15,6 +15,8 @@ #include "shoes/types/download.h" #include "shoes/canvas.h" +extern int shoes_cache_setting; + void shoes_download(shoes_http_request *req) { /* monkey patched out in download.rb - need function for linking */ printf("monkey patch failed\n"); @@ -23,7 +25,6 @@ void shoes_download(shoes_http_request *req) { void shoes_queue_download(shoes_http_request *req) { //printf("shoes_queue_download called: %s -> %s\n",req->url,req->filepath); VALUE path, url, opts, svsym, dnobj, stvar; - // convert req->url, req->filepath to ruby strings path = rb_str_new2(req->filepath); url = rb_str_new2(req->url); // make a hash @@ -41,8 +42,6 @@ void shoes_queue_download(shoes_http_request *req) { // dig the etag field out of headers. VALUE hdrs = rb_funcall(dnobj, rb_intern("headers"), 0); //printf("returned from image_download_sync %i\n", st); - // shoes_image_download_event *idat - // shoes_image_download_event in req->data shoes_image_download_event *side = req->data; side->status = st; VALUE etag = Qnil; @@ -54,11 +53,9 @@ void shoes_queue_download(shoes_http_request *req) { etag = key; break; } - //printf("key=%s\n",RSTRING_PTR(key)); } side->etag = RSTRING_PTR(rb_hash_aref(hdrs, etag)); - //VALUE rbetag = rb_hash_aref(hdrs, rb_intern("etag")); - //printf("%s\n",RSTRING_PTR(rbetag)); + // draws the image into side->slot shoes_catch_message(SHOES_IMAGE_DOWNLOAD, Qnil, side); shoes_http_request_free(req); free(req); @@ -66,6 +63,45 @@ void shoes_queue_download(shoes_http_request *req) { // after this stack frame pops there's nothing holding them. } +shoes_cached_image* shoes_no_cache_queue_download(shoes_http_request *req) { + VALUE path, url, opts, svsym, dnobj, stvar; + path = rb_str_new2(req->filepath); + url = rb_str_new2(req->url); + opts = rb_hash_new(); + shoes_cached_image *cached; + // make a :save symbol + svsym = ID2SYM(rb_intern("save")); + // add :save and filepath to hash + rb_hash_aset(opts, svsym, path); + // Call Shoes::image_download_sync - defined in image.rb + dnobj = rb_funcall(cShoes, + rb_intern("image_download_sync"), 2, url, opts); + // convert the status var of dnobj to a C int and save it in req->idat + stvar = rb_funcall(dnobj, rb_intern("status"), 0); + int st = NUM2INT(stvar); + // dig the etag field out of headers. + VALUE hdrs = rb_funcall(dnobj, rb_intern("headers"), 0); + shoes_image_download_event *side = req->data; + side->status = st; + VALUE etag = Qnil; + VALUE keys = rb_funcall(hdrs, s_keys, 0); + int i; + for (i = 0; i < RARRAY_LEN(keys); i++ ) { + VALUE key = rb_ary_entry(keys, i); + if (strcmp("etag", RSTRING_PTR(key)) == 0) { + etag = key; + break; + } + } + side->etag = RSTRING_PTR(rb_hash_aref(hdrs, etag)); + // may draw the image into side->slot + cached = shoes_no_cache_download(side); + shoes_http_request_free(req); + free(req); + return cached; +} + + VALUE shoes_http_err(SHOES_DOWNLOAD_ERROR code) { /* a little unclear what this does or what it returns I think it converts the platform 'code' to a Shoes string diff --git a/shoes/image.c b/shoes/image.c index 4da78ef5..da7ea8ac 100644 --- a/shoes/image.c +++ b/shoes/image.c @@ -36,6 +36,8 @@ shoes_image_format shoes_image_detect(VALUE, int *, int *); +int shoes_cache_setting = 1; + #define JPEG_LINES 16 #define SIZE_SURFACE ((cairo_surface_t *)1) @@ -818,6 +820,26 @@ shoes_code shoes_load_imagesize(VALUE imgpath, int *width, int *height) { return SHOES_OK; } +shoes_cached_image* shoes_no_cache_download(shoes_image_download_event *evt) { + int width, height; + shoes_cached_image *cached = shoes_world->blank_cache; + fprintf(stderr,"http no cache, finishing\n"); + if (evt->status != 200) + shoes_error("Shoes could not load the file at %s. [%lu]", evt->uripath, evt->status); + cairo_surface_t *img = shoes_surface_create_from_file(rb_str_new2(evt->filepath), &width, &height); + if (img != shoes_world->blank_image) { + cached = shoes_cached_image_new(width, height, img); + } + shoes_canvas_repaint_all(evt->slot); + + free(evt->filepath); + free(evt->uripath); + if (evt->etag != NULL) + free(evt->etag); + free(evt); + return cached; +} + unsigned char shoes_image_downloaded(shoes_image_download_event *idat) { int i, j, width, height; SHA1_CTX context; @@ -900,29 +922,28 @@ int shoes_http_image_handler(shoes_http_event *de, void *data) { return SHOES_DOWNLOAD_CONTINUE; } -shoes_cached_image *shoes_load_image(VALUE slot, VALUE imgpath, VALUE nocache) { +shoes_cached_image *shoes_load_image(VALUE slot, VALUE imgpath, VALUE cache_opt) { shoes_cached_image *cached = NULL; cairo_surface_t *img = NULL; VALUE filename = rb_funcall(imgpath, s_downcase, 0); StringValue(filename); char *fname = RSTRING_PTR(filename); int width = 1, height = 1; - /* - * don't check the cache issue #379 always download - it will be cached - * but we will ignore it. Brilliant fix or sub-optimal hack? - */ - if (nocache == Qnil || nocache == Qfalse) { + int global_cache_save = shoes_cache_setting; // save global setting + shoes_cache_setting = (cache_opt == Qtrue) ? 1 : 0; + + if (cache_opt == Qtrue) { if (shoes_cache_lookup(RSTRING_PTR(imgpath), &cached)) { - fprintf(stderr, "using cached image\n"); - goto done; - } + // fprintf(stderr, "using memory cached image\n"); + goto done; + } } else { // fall thru - fprintf(stderr, "always (down)load\n"); + // fprintf(stderr, "always (down)load\n"); } if (strlen(fname) > 7 && (strncmp(fname, "http://", 7) == 0 || strncmp(fname, "https://", 8) == 0)) { struct timeval tv; - VALUE cache, uext, hdrs, tmppath, uri, scheme, host, port, requ, path, cachepath = Qnil, hash = Qnil; + VALUE cache, uext, hdrs, tmppath, uri, scheme, host, port, requ, path, cachepath = Qnil, digest = Qnil; rb_require("shoes/data"); uri = rb_funcall(cShoes, rb_intern("uri"), 1, imgpath); scheme = rb_funcall(uri, s_scheme, 0); @@ -931,31 +952,43 @@ shoes_cached_image *shoes_load_image(VALUE slot, VALUE imgpath, VALUE nocache) { requ = rb_funcall(uri, s_request_uri, 0); path = rb_funcall(uri, s_path, 0); path = rb_funcall(path, s_downcase, 0); - // check the download cache (~/.shoes/+cache) - cache = rb_funcall(rb_const_get(rb_cObject, rb_intern("DATABASE")), rb_intern("check_cache_for"), 1, imgpath); - uext = rb_funcall(rb_cFile, rb_intern("extname"), 1, path); - hdrs = Qnil; - if (!NIL_P(cache)) { - VALUE etag = rb_hash_aref(cache, ID2SYM(rb_intern("etag"))); - hash = rb_hash_aref(cache, ID2SYM(rb_intern("hash"))); - if (!NIL_P(hash)) - cachepath = rb_funcall(cShoes, rb_intern("image_cache_path"), 2, hash, uext); - int saved = NUM2INT(rb_hash_aref(cache, ID2SYM(rb_intern("saved")))); - gettimeofday(&tv, 0); - if (tv.tv_sec - saved < SHOES_IMAGE_EXPIRE) { - fprintf(stderr,"recursive load\n"); - cached = shoes_load_image(slot, cachepath, nocache); // recursive ick! - if (cached != NULL) { - shoes_cache_insert(SHOES_CACHE_ALIAS, imgpath, cached); - goto done; - } - } else if (!NIL_P(etag)) - rb_hash_aset(hdrs = rb_hash_new(), rb_str_new2("If-None-Match"), etag); + if (cache_opt != Qtrue) { + /* populate the Values used later for a download - be careful + * tmppath calculated later is good enough. Remember, curl could + * might be used again instead of rbload + */ + hdrs = Qnil; + cachepath = Qnil; + digest = Qnil; // a hexdigest hash - not a ruby {} + uext = rb_funcall(rb_cFile, rb_intern("extname"), 1, path); + } else { + // check the download cache (~/.shoes/+cache) + cache = rb_funcall(rb_const_get(rb_cObject, rb_intern("DATABASE")), rb_intern("check_cache_for"), 1, imgpath); + uext = rb_funcall(rb_cFile, rb_intern("extname"), 1, path); + hdrs = Qnil; + if (!NIL_P(cache)) { + VALUE etag = rb_hash_aref(cache, ID2SYM(rb_intern("etag"))); + digest = rb_hash_aref(cache, ID2SYM(rb_intern("hash"))); + if (!NIL_P(digest)) + cachepath = rb_funcall(cShoes, rb_intern("image_cache_path"), 2, digest, uext); + int saved = NUM2INT(rb_hash_aref(cache, ID2SYM(rb_intern("saved")))); + gettimeofday(&tv, 0); + if (tv.tv_sec - saved < SHOES_IMAGE_EXPIRE) { + //fprintf(stderr,"old cache, recursive load\n"); + cached = shoes_load_image(slot, cachepath, Qtrue); // recursive ick! + if (cached != NULL) { + shoes_cache_insert(SHOES_CACHE_ALIAS, imgpath, cached); + goto done; + } + } else if (!NIL_P(etag)) + rb_hash_aset(hdrs = rb_hash_new(), rb_str_new2("If-None-Match"), etag); + } + cached = shoes_cached_image_new(1, 1, shoes_world->blank_image); + if (! NIL_P(cache_opt)) + shoes_cache_insert(SHOES_CACHE_FILE, imgpath, cached); } - cached = shoes_cached_image_new(1, 1, shoes_world->blank_image); - shoes_cache_insert(SHOES_CACHE_FILE, imgpath, cached); + // build the req for a download tmppath = rb_funcall(cShoes, rb_intern("image_temp_path"), 2, uri, uext); - shoes_http_request *req = SHOE_ALLOC(shoes_http_request); SHOE_MEMZERO(req, shoes_http_request, 1); shoes_image_download_event *idat = SHOE_ALLOC(shoes_image_download_event); @@ -970,25 +1003,41 @@ shoes_cached_image *shoes_load_image(VALUE slot, VALUE imgpath, VALUE nocache) { idat->filepath = strdup(RSTRING_PTR(tmppath)); idat->uripath = strdup(RSTRING_PTR(imgpath)); idat->slot = slot; - if (!NIL_P(cachepath)) idat->cachepath = strdup(RSTRING_PTR(cachepath)); - if (!NIL_P(hash)) SHOE_MEMCPY(idat->hexdigest, RSTRING_PTR(hash), char, min(42, RSTRING_LEN(hash))); - if (!NIL_P(hdrs)) req->headers = shoes_http_headers(hdrs); + if (!NIL_P(cachepath)) + idat->cachepath = strdup(RSTRING_PTR(cachepath)); + if (!NIL_P(digest)) + SHOE_MEMCPY(idat->hexdigest, RSTRING_PTR(digest), char, min(42, RSTRING_LEN(digest))); + if (!NIL_P(hdrs)) + req->headers = shoes_http_headers(hdrs); req->data = idat; - shoes_queue_download(req); + + if (shoes_cache_setting) { + /* + * shoes_queue_download does the download, optional cache settings + * and display of image in the slot. + */ + shoes_queue_download(req); + } else { + // Make sure cached varible points to mething useful when non cached? + cached = shoes_no_cache_queue_download(req); + } goto done; + } - /* here when not http reading from file */ - fprintf(stderr, "Read file\n"); + + /* here when reading from file */ + //fprintf(stderr, "Read file %s\n",RSTRING_PTR(imgpath)); img = shoes_surface_create_from_file(imgpath, &width, &height); if (img != shoes_world->blank_image) { cached = shoes_cached_image_new(width, height, img); - if (nocache != Qtrue) + if (cache_opt == Qtrue) shoes_cache_insert(SHOES_CACHE_FILE, imgpath, cached); } done: if (cached == NULL) cached = shoes_world->blank_cache; + shoes_cache_setting = global_cache_save; // restore global setting return cached; } diff --git a/shoes/types/download.c b/shoes/types/download.c index 12bbb5d3..86e50eb7 100644 --- a/shoes/types/download.c +++ b/shoes/types/download.c @@ -2,7 +2,7 @@ // ruby VALUE cDownload, cResponse; - +extern int shoes_cache_setting; FUNC_M("+download", download, -1); EVENT_COMMON(http, http_klass, start); @@ -34,6 +34,8 @@ void shoes_download_init() { } // ruby (download) +extern int shoes_cache_setting; + void shoes_http_mark(shoes_http_klass *dl) { rb_gc_mark_maybe(dl->parent); rb_gc_mark_maybe(dl->attr); @@ -251,44 +253,48 @@ VALUE shoes_response_status(VALUE self) { return rb_iv_get(self, "status"); } -// TODO: handle exceptions +/* TODO: check for C memory leaks + * This a lot more than 'catch' - that's a poor name + * + */ int shoes_catch_message(unsigned int name, VALUE obj, void *data) { int ret = SHOES_DOWNLOAD_CONTINUE; switch (name) { - case SHOES_THREAD_DOWNLOAD: - ret = shoes_message_download(obj, data); - free(data); - break; - case SHOES_IMAGE_DOWNLOAD: { - VALUE hash, etag = Qnil, uri, uext, path, realpath; - shoes_image_download_event *side = (shoes_image_download_event *)data; - if (shoes_image_downloaded(side)) { - shoes_canvas_repaint_all(side->slot); - - path = rb_str_new2(side->filepath); - uri = rb_str_new2(side->uripath); - hash = rb_str_new2(side->hexdigest); - if (side->etag != NULL) etag = rb_str_new2(side->etag); - uext = rb_funcall(rb_cFile, rb_intern("extname"), 1, path); - - rb_funcall(rb_const_get(rb_cObject, rb_intern("DATABASE")), - rb_intern("notify_cache_of"), 3, uri, etag, hash); - if (side->status != 304) { - realpath = rb_funcall(cShoes, rb_intern("image_cache_path"), 2, hash, uext); - rename(side->filepath, RSTRING_PTR(realpath)); - } - } - - free(side->filepath); - free(side->uripath); - if (side->etag != NULL) free(side->etag); - free(data); - } - break; - } - return ret; + case SHOES_THREAD_DOWNLOAD: + ret = shoes_message_download(obj, data); + free(data); + break; + case SHOES_IMAGE_DOWNLOAD: { + VALUE hash, etag = Qnil, uri, uext, path, realpath; + shoes_image_download_event *side = (shoes_image_download_event *)data; + if (shoes_image_downloaded(side)) { + shoes_canvas_repaint_all(side->slot); + path = rb_str_new2(side->filepath); + uri = rb_str_new2(side->uripath); + hash = rb_str_new2(side->hexdigest); + if (side->etag != NULL) + etag = rb_str_new2(side->etag); + uext = rb_funcall(rb_cFile, rb_intern("extname"), 1, path); + rb_funcall(rb_const_get(rb_cObject, rb_intern("DATABASE")), + rb_intern("notify_cache_of"), 3, uri, etag, hash); + if (side->status != 304) { + realpath = rb_funcall(cShoes, rb_intern("image_cache_path"), 2, hash, uext); + rename(side->filepath, RSTRING_PTR(realpath)); + } + } + + free(side->filepath); + free(side->uripath); + if (side->etag != NULL) + free(side->etag); + free(data); + } + break; + } + return ret; } + // canvas VALUE shoes_canvas_download(int argc, VALUE *argv, VALUE self) { VALUE url, block, obj, attr = Qnil; diff --git a/shoes/types/image.c b/shoes/types/image.c index b279de77..c40bcd02 100644 --- a/shoes/types/image.c +++ b/shoes/types/image.c @@ -144,6 +144,7 @@ void shoes_image_free(shoes_image *image) { shoes_transform_release(image->st); RUBY_CRITICAL(SHOE_FREE(image)); } +extern int shoes_cache_setting; VALUE shoes_image_new(VALUE klass, VALUE path, VALUE attr, VALUE parent, shoes_transform *st) { VALUE obj = Qnil; @@ -159,18 +160,18 @@ VALUE shoes_image_new(VALUE klass, VALUE path, VALUE attr, VALUE parent, shoes_t image->attr = attr; image->parent = shoes_find_canvas(parent); COPY_PENS(image->attr, basic->attr); - VALUE nocache = Qfalse; // note reversed meaning internally + VALUE cache_opt = shoes_cache_setting ? Qtrue : Qfalse; VALUE vcache = shoes_hash_get(attr,rb_intern("cache")); if (! NIL_P(vcache)) { - // arg specified. reverse it. - if (vcache == Qnil || vcache == Qfalse) { - nocache = Qtrue; - fprintf(stderr, "don't cache\n"); - } + cache_opt = vcache; + } + /* + if (cache_opt == Qnil || cache_opt == Qfalse) { + fprintf(stderr,"won't cache\n"); } else { fprintf(stderr,"will cache\n"); } - + */ if (rb_obj_is_kind_of(path, cImage)) { shoes_image *image2; Data_Get_Struct(path, shoes_image, image2); @@ -179,7 +180,7 @@ VALUE shoes_image_new(VALUE klass, VALUE path, VALUE attr, VALUE parent, shoes_t } else if (!NIL_P(path)) { path = shoes_native_to_s(path); image->path = path; - image->cached = shoes_load_image(image->parent, path, nocache); + image->cached = shoes_load_image(image->parent, path, cache_opt); image->type = SHOES_CACHE_FILE; } else { shoes_canvas *canvas; diff --git a/shoes/types/pattern.c b/shoes/types/pattern.c index d400b1fa..4c1c062d 100644 --- a/shoes/types/pattern.c +++ b/shoes/types/pattern.c @@ -37,6 +37,9 @@ void shoes_pattern_init() { } // ruby + +extern int shoes_cache_setting; + void shoes_pattern_mark(shoes_pattern *pattern) { rb_gc_mark_maybe(pattern->source); rb_gc_mark_maybe(pattern->parent); @@ -95,7 +98,8 @@ VALUE shoes_pattern_set_fill(VALUE self, VALUE source) { if (rb_obj_is_kind_of(source, cColor)) { pattern->pattern = shoes_color_pattern(source); } else { - pattern->cached = shoes_load_image(pattern->parent, source, Qfalse); + pattern->cached = shoes_load_image(pattern->parent, source, + (shoes_cache_setting ? Qtrue: Qnil)); if (pattern->cached != NULL && pattern->cached->pattern == NULL) pattern->cached->pattern = cairo_pattern_create_for_surface(pattern->cached->surface); }