1 /* load.c: Load and draw routines for libRUIN
2  * Copyright (C) 2011 Julian Graham
3  *
4  * libRUIN is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include <assert.h>
19 #include <curses.h>
20 #include <libguile.h>
21 #include <limits.h>
22 #include <signal.h>
23 #include <stdlib.h>
24 #include <string.h>
25 
26 #include "api/api.h"
27 #include "debug.h"
28 #include "handlers/handlers.h"
29 #include "layout.h"
30 #include "load.h"
31 #include "parse.h"
32 #include "render.h"
33 #include "scheme.h"
34 #include "util.h"
35 #include "window.h"
36 #include "xml.h"
37 
38 #define GUILE_LOAD_PATH "%load-path"
39 
40 static int ruin_initialized = FALSE;
41 static struct sigaction ruin_sigaction_old_action;
42 
43 ruin_windows_t *_ruin_windows = NULL;
44 
45 extern void _ruin_scm_init_api ();
46 extern void _ruin_scm_init_handlers ();
47 extern void _ruin_box_init ();
48 extern void _ruin_css_init ();
49 
ruin_load_layout_and_render(ruin_window_t * w,ruin_node_t * t)50 long ruin_load_layout_and_render (ruin_window_t *w, ruin_node_t *t)
51 {
52   long start_time = ruin_util_current_time_millis ();
53   GList *root_boxes_ptr = NULL;
54 
55   w->root_boxes = ruin_layout_generate_and_layout_elements (w, t);
56   root_boxes_ptr = w->root_boxes;
57 
58   wclear (w->window);
59 
60   while (root_boxes_ptr != NULL)
61     {
62       ruin_box_t *root_box = (ruin_box_t *) root_boxes_ptr->data;
63 
64       ruin_debug_print_box_tree (root_box);
65       ruin_render_render_tree (root_box);
66 
67       root_boxes_ptr = root_boxes_ptr->next;
68     }
69 
70   wrefresh (w->window);
71 
72   return ruin_util_current_time_millis () - start_time;
73 }
74 
ruin_draw(ruin_window_t * w,SCM doc)75 int ruin_draw (ruin_window_t *w, SCM doc)
76 {
77   ruin_node_t *tree = NULL;
78   int top, left, bottom, right;
79 
80   SCM cascade;
81   SCM selection_context;
82 
83   enum ruin_xml_dialect lang = ruin_parse_determine_dialect (w, doc);
84 
85   cascade = ruin_scheme_scss_make_cascade (w);
86   scm_gc_protect_object (cascade);
87 
88   selection_context = ruin_scheme_scss_make_selection_context
89     (w, ruin_scheme_scss_document_interface_sdom,
90      ruin_scheme_scss_make_rendering_interface
91      (w, scm_c_eval_string ("ruin:scss-pseudo-class-handler"),
92       scm_c_eval_string ("ruin:scss-pseudo-element-handler")),
93      cascade);
94   scm_gc_protect_object (selection_context);
95 
96   w->cascade = cascade;
97   w->selection_context = selection_context;
98 
99   ruin_window_clear (w);
100 
101   /* Generate default stylesheet.  If the document has an attached stylesheet
102      or adds style on a per-element basis, we can apply that on top of the
103      default. */
104 
105   getbegyx (w->window, top, left);
106   getmaxyx (w->window, bottom, right);
107 
108   ruin_util_log (w, "window dimensions are %d, %d", right, bottom);
109 
110   switch (lang) {
111   case RUIN_XML_DIALECT_XUL:
112     ruin_scheme_scss_set_cascade_agent
113       (w, cascade, scm_copy_tree (_ruin_windows->xul_agent_css));
114     break;
115   case RUIN_XML_DIALECT_XHTML:
116     ruin_scheme_scss_set_cascade_agent
117       (w, cascade, scm_copy_tree (_ruin_windows->xhtml_agent_css));
118   default:
119     break;
120   }
121 
122   tree = ruin_parse_document (w, doc);
123 
124   ruin_scheme_sdom_dispatch_event (w, tree->doc, "load");
125   ruin_util_log (w, "total time %ldms", ruin_load_layout_and_render (w, tree));
126 
127   return TRUE;
128 }
129 
ruin_draw_string(ruin_window_t * w,char * doc)130 int ruin_draw_string(ruin_window_t *w, char *doc) {
131   if (doc != NULL) {
132     SCM dom_doc = ruin_scheme_sdom_xml_to_sdom
133       (w, scm_open_input_string(scm_from_locale_string(doc)), SCM_EOL);
134     return ruin_draw(w, dom_doc);
135   }
136   return FALSE;
137 }
138 
ruin_draw_file(ruin_window_t * w,char * filename)139 int ruin_draw_file(ruin_window_t *w, char *filename) {
140   if (filename != NULL) {
141     char *abs_path = ruin_util_get_parent_directory(filename);
142     SCM dom_doc = ruin_scheme_sdom_xml_to_sdom
143       (w, scm_open_file(scm_from_locale_string(filename),
144 			scm_from_locale_string("r")),
145        SCM_EOL);
146     scm_call_2(scm_c_eval_string("sdom:set-document-uri!"), dom_doc,
147 	       scm_string_append(scm_list_2(scm_from_locale_string("file://"),
148 					    scm_from_locale_string(abs_path))));
149     free(abs_path);
150     return ruin_draw(w, dom_doc);
151   }
152   return FALSE;
153 }
154 
_set_ruin_temp_load_path(SCM oldval,char * varname)155 void _set_ruin_temp_load_path(SCM oldval, char *varname) {
156   char *tmpval = getenv(varname);
157   scm_set_car_x(scm_c_eval_string(GUILE_LOAD_PATH),
158 		scm_from_locale_string
159 		(tmpval != NULL ? tmpval : RUIN_SCHEME_PATH));
160   scm_set_cdr_x(scm_c_eval_string(GUILE_LOAD_PATH), oldval);
161   return;
162 }
163 
ruin_init()164 int ruin_init() {
165   SCM old_path = scm_list_copy(scm_c_eval_string(GUILE_LOAD_PATH));
166   extern pthread_mutex_t _ruin_util_id_lock;
167 
168   struct sigaction new_action;
169   new_action.sa_sigaction = ruin_window_signal_handler_SIGWINCH;
170   new_action.sa_flags = SA_SIGINFO;
171   sigemptyset(&new_action.sa_mask);
172 
173   if (ruin_initialized)
174     return FALSE;
175 
176   pthread_mutex_init(&_ruin_util_id_lock, NULL);
177   (void) sigaction(SIGWINCH, NULL, &ruin_sigaction_old_action);
178   (void) sigaction(SIGWINCH, &new_action, NULL);
179 
180   /*
181    * Upon load, `(rnrs exceptions)' registers the r6rs:exception
182    * exception-printer.
183    */
184 
185   scm_c_use_module("rnrs exceptions");
186 
187   _set_ruin_temp_load_path(old_path, "RUIN_SCHEME_SXML_PATH");
188   scm_c_use_module("sxml ssax");
189 
190   _set_ruin_temp_load_path(old_path, "RUIN_SCHEME_SDOM_PATH");
191   scm_c_use_module("sdom core");
192   scm_c_use_module("sdom events");
193 
194   _set_ruin_temp_load_path(old_path, "RUIN_SCHEME_SCSS_PATH");
195   scm_c_use_module("scss scss");
196   scm_c_use_module("scss interface sdom");
197   scm_set_car_x(scm_c_eval_string(GUILE_LOAD_PATH), SCM_CAR(old_path));
198   scm_set_cdr_x(scm_c_eval_string(GUILE_LOAD_PATH), SCM_CDR(old_path));
199 
200   ruin_parse_init ();
201 
202   /* Also initialize our own Scheme API. */
203 
204   ruin_scheme_init ();
205   _ruin_scm_init_api ();
206   _ruin_scm_init_handlers ();
207   _ruin_css_init ();
208   _ruin_box_init ();
209 
210   /* Set up a default color pair... */
211   init_pair(1, COLOR_WHITE, COLOR_BLACK);
212 
213   _ruin_windows = ruin_windows_new();
214   return TRUE;
215 }
216 
ruin_shutdown()217 void ruin_shutdown() {
218   extern pthread_mutex_t _ruin_util_id_lock;
219   if (!ruin_initialized)
220     return;
221   pthread_mutex_destroy(&_ruin_util_id_lock);
222   ruin_windows_free(_ruin_windows);
223   (void) sigaction(SIGWINCH, &ruin_sigaction_old_action, NULL);
224   return;
225 }
226