/* load.c: Load and draw routines for libRUIN * Copyright (C) 2007 Julian Graham * * libRUIN 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; either version 2 of the License, or * (at your option) any later version. * * libRUIN 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 libRUIN; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include "api/api.h" #include "api/win-fns.h" #include "handlers/handlers.h" #include "layout.h" #include "load.h" #include "render.h" #include "scheme.h" #include "util.h" #include "window.h" #include "xhtml.h" #include "xul.h" #define GUILE_LOAD_PATH "%load-path" static int ruin_initialized = FALSE; static struct sigaction ruin_sigaction_old_action; ruin_windows_t *_ruin_windows = NULL; extern void _ruin_scm_init_api(); extern void _ruin_scm_init_handlers(); static void _install_default_handler(SCM node, char *event, char *handler) { scm_apply_0(scm_c_eval_string("sdom:add-event-listener!"), scm_list_5(node, scm_str2symbol(event), scm_makfrom0str("default"), scm_c_eval_string(handler), SCM_BOOL_T)); } long ruin_load_layout_and_render(ruin_window_t *w) { long start_time = ruin_util_current_time_millis(); ruin_util_list *inheritance = ruin_util_list_new(w->top); ruin_layout_normalize_lengths(w->top, inheritance, 0); ruin_layout_size_tree (w->top->first_child, inheritance, w->top->top, w->top->left); free(inheritance); /* We don't set the pseudo-element handler until render time, because otherwise we wind up with a recursive overflow... */ scm_call_1(scm_c_eval_string("scss:set-pseudo-element-handler!"), scm_c_eval_string("ruin:scss-pseudo-element-handler")); { SCM root = w->top->first_child->element; _install_default_handler (root, "sdom:event-keyup", "ruin:default-tab-handler"); _install_default_handler(root, "sdom:event-dom-attr-modified", "ruin:default-attr-modified-handler"); _install_default_handler(root, "sdom:event-dom-node-inserted", "ruin:default-node-insertion-handler"); _install_default_handler(root, "sdom:event-dom-node-removed", "ruin:default-node-removal-handler"); } wclear(w->window); ruin_render_render_tree(w->top->first_child, NULL); wrefresh(w->window); return ruin_util_current_time_millis() - start_time; } int ruin_draw(ruin_window_t *w, SCM doc) { ruin_element_t *top_level; ruin_element_t *tree = NULL; int top, left, bottom, right; SCM element; SCM cascade; enum ruin_layout_xml_dialect lang = RUIN_LAYOUT_XML_DIALECT_XHTML; SCM dtd = scm_call_2(scm_c_eval_string("sdom:get-dom-property"), doc, scm_makfrom0str("sdom:doc-type")); if (dtd == SCM_BOOL_F) { ruin_util_log(w, "could not determine xhtml dialect; assuming xhtml"); } else { char *type = ruin_scheme_sdom_get_dom_property(dtd, "sdom:name"); if (strcmp(type, "xhtml") == 0 || strcmp(type, "html") == 0) { lang = RUIN_LAYOUT_XML_DIALECT_XHTML; } else if (strcmp(type, "xul") == 0) { lang = RUIN_LAYOUT_XML_DIALECT_XUL; } else { ruin_util_log(w, "unknown xml dialect %s", type); return -1; } } cascade = scm_call_0(scm_c_eval_string("scss:create-cascade")); scm_gc_protect_object(cascade); ruin_window_clear(w); /* Generate default stylesheet. If the document has an attached stylesheet or adds style on a per-element basis, we can apply that on top of the default. */ getbegyx(w->window, top, left); getmaxyx(w->window, bottom, right); ruin_util_log(w, "window dimensions are %d, %d", right, bottom); top_level = ruin_element_new(); top_level->dialect = lang; top_level->cascade = cascade; top_level->ids = w->ids; top_level->parent_window = w; top_level->top = top; top_level->left = left; top_level->height.computed = bottom - top; top_level->width.computed = right - left; top_level->max_width = top_level->width; top_level->max_height = top_level->height; top_level->padding_left.computed = 0; top_level->padding_right.computed = 0; ruin_layout_add_style (&top_level->inherent_attribute_style, "display", "block"); top_level->doc = doc; top_level->element = scm_makfrom0str("ruin-document-root"); scm_gc_protect_object(top_level->element); element = scm_call_2(scm_c_eval_string("sdom:get-dom-property"), doc, scm_makfrom0str("sdom:document-element")); switch(lang) { case RUIN_LAYOUT_XML_DIALECT_XUL: scm_call_2(scm_c_eval_string("scss:set-agent-stylesheet!"), cascade, scm_copy_tree(_ruin_windows->xul_agent_css)); tree = ruin_xul_generate_tree(w, element, top_level, NULL); break; case RUIN_LAYOUT_XML_DIALECT_XHTML: scm_call_2(scm_c_eval_string("scss:set-agent-stylesheet!"), cascade, scm_copy_tree(_ruin_windows->xhtml_agent_css)); tree = ruin_xhtml_generate_tree(w, element, top_level, NULL); default: break; } scm_call_2(scm_c_eval_string("sdom:dispatch-event"), w->top->element, scm_str2symbol("sdom:event-load")); top_level->first_child = tree; top_level->first_child->element = element; w->top = top_level; ruin_util_log(w, "total time %ldms", ruin_load_layout_and_render(w)); return TRUE; } int ruin_draw_string(ruin_window_t *w, char *doc) { if (doc != NULL) { SCM dom_doc = scm_call_2(scm_c_eval_string("sdom:xml->sdom"), scm_call_1(scm_c_eval_string("open-input-string"), scm_makfrom0str(doc)), scm_c_eval_string("'()")); scm_gc_protect_object(dom_doc); return ruin_draw(w, dom_doc); } return FALSE; } int ruin_draw_file(ruin_window_t *w, char *filename) { if (filename != NULL) { char *abs_path = ruin_util_get_parent_directory(filename); SCM dom_doc = scm_call_2(scm_c_eval_string("sdom:xml->sdom"), scm_call_1(scm_c_eval_string("open-input-file"), scm_makfrom0str(filename)), scm_c_eval_string("'()")); scm_gc_protect_object(dom_doc); scm_call_3(scm_c_eval_string("sdom:set-dom-property!"), dom_doc, scm_makfrom0str("sdom:document-uri"), scm_string_append(scm_list_2(scm_makfrom0str("file://"), scm_makfrom0str(abs_path)))); free(abs_path); return ruin_draw(w, dom_doc); } return FALSE; } void _set_ruin_temp_load_path(SCM oldval, char *varname) { char *tmpval = getenv(varname); scm_set_car_x(scm_c_eval_string(GUILE_LOAD_PATH), scm_makfrom0str(tmpval != NULL ? tmpval : RUIN_SCHEME_PATH)); scm_set_cdr_x(scm_c_eval_string(GUILE_LOAD_PATH), oldval); return; } int ruin_init() { SCM old_path = scm_list_copy(scm_c_eval_string(GUILE_LOAD_PATH)); extern pthread_mutex_t _ruin_util_id_lock; struct sigaction new_action; new_action.sa_sigaction = ruin_window_signal_handler_SIGWINCH; new_action.sa_flags = SA_SIGINFO; sigemptyset(&new_action.sa_mask); if (ruin_initialized) return FALSE; pthread_mutex_init(&_ruin_util_id_lock, NULL); (void) sigaction(SIGWINCH, NULL, &ruin_sigaction_old_action); (void) sigaction(SIGWINCH, &new_action, NULL); scm_c_use_module("ice-9 regex"); _set_ruin_temp_load_path(old_path, "RUIN_SCHEME_SXML_PATH"); scm_c_use_module("sxml ssax"); /* scm_c_use_module("sxml xpath"); */ _set_ruin_temp_load_path(old_path, "RUIN_SCHEME_SDOM_PATH"); scm_c_use_module("sdom core"); scm_c_use_module("sdom events"); _set_ruin_temp_load_path(old_path, "RUIN_SCHEME_SCSS_PATH"); scm_c_use_module("scss scss"); scm_set_car_x(scm_c_eval_string(GUILE_LOAD_PATH), SCM_CAR(old_path)); scm_set_cdr_x(scm_c_eval_string(GUILE_LOAD_PATH), SCM_CDR(old_path)); /* Also initialize our own Scheme API. */ ruin_scheme_init(); _ruin_scm_init_api(); _ruin_scm_init_handlers(); scm_call_1(scm_c_eval_string("scss:set-sxml-parent-function!"), scm_c_eval_string("(lambda (d n) (sdom:get-dom-property n " "\"sdom:parent-node\"))")); scm_call_1(scm_c_eval_string("scss:set-dot-handler!"), scm_c_eval_string("(lambda (s d n) (and (eqv? " "(sdom:get-dom-property n \"sdom:node-type\") " "sdom:node-type-element) (equal? (car s) " "(sdom:get-attribute n \"class\"))))")); scm_call_1(scm_c_eval_string("scss:set-id-handler!"), scm_c_eval_string("(lambda (s d n) (and (eqv? " "(sdom:get-dom-property n \"sdom:node-type\") " "sdom:node-type-element) (equal? s " "(sdom:get-attribute n \"id\"))))")); scm_call_1(scm_c_eval_string("scss:set-pseudo-class-handler!"), scm_c_eval_string("ruin:scss-pseudo-class-handler")); /* Set up a default color pair... */ init_pair(1, COLOR_WHITE, COLOR_BLACK); _ruin_windows = ruin_windows_new(); return TRUE; } void ruin_shutdown() { extern pthread_mutex_t _ruin_util_id_lock; if (!ruin_initialized) return; pthread_mutex_destroy(&_ruin_util_id_lock); ruin_windows_free(_ruin_windows); (void) sigaction(SIGWINCH, &ruin_sigaction_old_action, NULL); return; }