1 /*
2    Florence - Florence is a simple virtual keyboard for Gnome.
3 
4    Copyright (C) 2012 François Agrech
5 
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2, or (at your option)
9    any later version.
10 
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15 
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software Foundation,
18    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 
20 */
21 
22 #include <unistd.h>
23 #include <sys/stat.h>
24 #include <glib.h>
25 #include <libxml/relaxng.h>
26 #include <libxml/xinclude.h>
27 #include "system.h"
28 #include "layoutreader.h"
29 #include "trace.h"
30 #include "settings.h"
31 
32 /* Open a layout element */
layoutreader_element_open(struct layout * layout,char * name)33 gboolean layoutreader_element_open(struct layout *layout, char *name)
34 {
35 	START_FUNC
36 	if (name) while (layout->cur && xmlStrcmp(layout->cur->name, (xmlChar *)name))
37 		layout->cur=layout->cur->next;
38 	if (layout->cur) layout->cur=layout->cur->children;
39 	END_FUNC
40 	return layout->cur==NULL?FALSE:TRUE;
41 }
42 
43 /* Close a layout element */
layoutreader_element_close(struct layout * layout)44 void layoutreader_element_close(struct layout *layout)
45 {
46 	START_FUNC
47 	if (layout->cur && layout->cur->parent) layout->cur=layout->cur->parent->next;
48 	END_FUNC
49 }
50 
51 /* Reset layout cursor */
layoutreader_reset(struct layout * layout)52 void layoutreader_reset(struct layout *layout)
53 {
54 	START_FUNC
55 	layout->cur=layout->doc->children;
56 	END_FUNC
57 }
58 
59 /* Initialize an element structure and look for it in the layout document */
layoutreader_element_init(struct layout * layout,char * element,size_t size)60 void *layoutreader_element_init(struct layout *layout, char *element, size_t size)
61 {
62 	START_FUNC
63 	void *ret=NULL;
64 	while (layout->cur && xmlStrcmp(layout->cur->name, (xmlChar *)element))
65 		layout->cur=layout->cur->next;
66 	if (layout->cur) {
67 		ret=g_malloc(size);
68 		memset(ret, 0, size);
69 		layout->cur=layout->cur->children;
70 	}
71 	END_FUNC
72 	return ret;
73 }
74 
75 /* Get double from layout text */
layoutreader_double_get(xmlDocPtr doc,xmlNodePtr cur)76 double layoutreader_double_get(xmlDocPtr doc, xmlNodePtr cur)
77 {
78 	START_FUNC
79 	double ret=0.0;
80 	xmlChar *tmp=xmlNodeListGetString(doc, cur->children, 1);
81 	ret=g_ascii_strtod((gchar *)tmp, NULL);
82 	xmlFree(tmp);
83 	END_FUNC
84 	return ret;
85 }
86 
87 /* Translate xml placement name to placement enumeration */
layoutreader_placement_get(struct layout * layout)88 enum layout_placement layoutreader_placement_get(struct layout *layout)
89 {
90 	START_FUNC
91 	enum layout_placement ret=LAYOUT_VOID;
92 	xmlChar *tmp=xmlNodeListGetString(layout->doc, layout->cur->children, 1);
93 	if (!xmlStrcmp(tmp, (xmlChar *)"left")) ret=LAYOUT_LEFT;
94 	else if (!xmlStrcmp(tmp, (xmlChar *)"right")) ret=LAYOUT_RIGHT;
95 	else if (!xmlStrcmp(tmp, (xmlChar *)"top")) ret=LAYOUT_TOP;
96 	else if (!xmlStrcmp(tmp, (xmlChar *)"bottom")) ret=LAYOUT_BOTTOM;
97 	else if (!xmlStrcmp(tmp, (xmlChar *)"over")) ret=LAYOUT_OVER;
98 	else flo_error(_("Unknown placement %s"), tmp);
99 	xmlFree(tmp);
100 	END_FUNC
101 	return ret;
102 }
103 
104 /* Update the string if the node lang matches locale */
layoutreader_update_lang(xmlDocPtr doc,xmlNodePtr node,char ** update)105 void layoutreader_update_lang(xmlDocPtr doc, xmlNodePtr node, char **update)
106 {
107 	START_FUNC
108 	xmlChar *lang=xmlNodeGetLang(node);
109 #ifdef HAVE_LOCALE_H
110 	if (!lang ||
111 		!xmlStrncmp(lang, (xmlChar *)setlocale(LC_MESSAGES, NULL),
112 		xmlStrlen(lang))) {
113 #else
114 	if (!lang) {
115 #endif
116 		if (*update) xmlFree(*update);
117 		*update=(char *)xmlNodeListGetString(doc, node, 1);
118 	}
119 	xmlFree(lang);
120 	END_FUNC
121 }
122 
123 /* dump svg from file */
124 char *layoutreader_svg_get(xmlDocPtr doc, xmlNodePtr cur)
125 {
126 	START_FUNC
127 	char *ret=NULL;
128 	xmlBufferPtr buf=xmlBufferCreate();
129 	xmlOutputBufferPtr outputbuf=xmlOutputBufferCreateBuffer(buf,
130 		xmlGetCharEncodingHandler(XML_CHAR_ENCODING_UTF8));
131 	xmlNodeDumpOutput(outputbuf, doc, cur, 0, 1, NULL);
132 	xmlOutputBufferFlush(outputbuf);
133 	ret=g_strdup((char *)xmlBufferContent(buf));
134 	xmlFree(outputbuf);
135 	xmlBufferFree(buf);
136 	END_FUNC
137 	return ret;
138 }
139 
140 /* Get the 'informatons' element data (see florence.c) */
141 struct layout_infos *layoutreader_infos_new(struct layout *layout)
142 {
143 	START_FUNC
144 	struct layout_infos *infos=layoutreader_element_init(layout,
145 		"informations", sizeof(struct layout_infos));
146 	xmlNodePtr cur=layout->cur;
147 	if (infos) for(;cur;cur=cur->next) {
148 		if (!xmlStrcmp(cur->name, (xmlChar *)"name")) {
149 			layoutreader_update_lang(layout->doc, cur->children, &infos->name);
150 		} else if (!xmlStrcmp(cur->name, (xmlChar *)"florence_version")) {
151 			infos->version=(char *)xmlNodeListGetString(layout->doc, cur->children, 1);
152 		}
153 	}
154 	layoutreader_element_close(layout);
155 	END_FUNC
156 	return infos;
157 }
158 
159 /* Free the 'informations' element data */
160 void layoutreader_infos_free(struct layout_infos *infos)
161 {
162 	START_FUNC
163 	if (infos) {
164 		if (infos->name) xmlFree(infos->name);
165 		if (infos->version) xmlFree(infos->version);
166 		g_free(infos);
167 	}
168 	END_FUNC
169 }
170 
171 /* Get the 'keyboard' element data (see keyboard.c) */
172 struct layout_size *layoutreader_keyboard_new(struct layout *layout)
173 {
174 	START_FUNC
175 	struct layout_size *size=layoutreader_element_init(layout,
176 		"keyboard", sizeof(struct layout_size));
177 	if (size) for(;layout->cur && xmlStrcmp(layout->cur->name, (xmlChar *)"key");
178 		layout->cur=layout->cur->next) {
179 		if (!xmlStrcmp(layout->cur->name, (xmlChar *)"width")) {
180 			size->w=layoutreader_double_get(layout->doc, layout->cur);
181 		} else if (!xmlStrcmp(layout->cur->name, (xmlChar *)"height")) {
182 			size->h=layoutreader_double_get(layout->doc, layout->cur);
183 		}
184 	}
185 	END_FUNC
186 	return size;
187 }
188 
189 /* Free the 'keyboard' element data */
190 void layoutreader_keyboard_free(struct layout *layout, struct layout_size *size)
191 {
192 	START_FUNC
193 	layoutreader_element_close(layout);
194 	if (size) g_free(size);
195 	END_FUNC
196 }
197 
198 /* Get the 'action' element data (for use in function layoutreader_key_new) */
199 void layoutreader_action_get(struct layout *layout, xmlNodePtr cur, unsigned char **action, unsigned char **argument)
200 {
201 	START_FUNC
202 	xmlNodePtr curact;
203 	for(curact=cur->children;curact;curact=curact->next) {
204 		if (!xmlStrcmp(curact->name, (xmlChar *)"command")) {
205 			*action=xmlNodeListGetString(layout->doc, curact->children, 1);
206 		} else if (!xmlStrcmp(curact->name, (xmlChar *)"argument")) {
207 			*argument=xmlNodeListGetString(layout->doc, curact->children, 1);
208 		}
209 	}
210 	if (!(*action))
211 		*action=xmlNodeListGetString(layout->doc, cur->children, 1);
212 	END_FUNC
213 }
214 
215 /* Get the 'key' element data (see key.c) */
216 struct layout_key *layoutreader_key_new(struct layout *layout, layout_modifier_cb mod_cb, void *object, void *xkb)
217 {
218 	START_FUNC
219 	xmlChar *tmp=NULL;
220 	xmlNodePtr cur=layout->cur;
221 	xmlNodePtr curmod;
222 	struct layout_key *key=layoutreader_element_init(layout, "key", sizeof(struct layout_key));
223 	struct layout_modifier *mod=g_malloc(sizeof(struct layout_modifier));
224 	xmlAttrPtr attr;
225 	struct layout_id *id;
226 
227 	if (key) {
228 		attr=xmlHasProp(layout->cur->parent, (xmlChar *)"id");
229 		if (attr) {
230 			id=g_malloc(sizeof(struct layout_id));
231 			id->object=object;
232 			id->name=(char *)xmlNodeListGetString(layout->doc, attr->children, 1);
233 			layout->ids=g_slist_append(layout->ids, id);
234 		}
235 		for(cur=layout->cur;cur;cur=cur->next) {
236 			mod->action=NULL;
237 			mod->argument=NULL;
238 			if (!xmlStrcmp(cur->name, (xmlChar *)"code")) {
239 				tmp=xmlNodeListGetString(layout->doc, cur->children, 1);
240 				mod->mod=0;
241 				mod->code=atoi((char *)tmp);
242 				mod_cb(mod, object, xkb);
243 				xmlFree(tmp);
244 			} else if (!xmlStrcmp(cur->name, (xmlChar *)"action")) {
245 				mod->mod=0;
246 				mod->code=0;
247 				layoutreader_action_get(layout, cur, &(mod->action), &(mod->argument));
248 				mod_cb(mod, object, xkb);
249 				if (mod->action) xmlFree(mod->action);
250 				if (mod->argument) xmlFree(mod->argument);
251 			} else if (!xmlStrcmp(cur->name, (xmlChar *)"modifier")) {
252 				mod->code=0;
253 				for(curmod=cur->children;curmod;curmod=curmod->next) {
254 					if (!xmlStrcmp(curmod->name, (xmlChar *)"code")) {
255 						tmp=xmlNodeListGetString(layout->doc, curmod->children, 1);
256 						mod->mod=atoi((char *)tmp);
257 						xmlFree(tmp);
258 					} else if (!xmlStrcmp(curmod->name, (xmlChar *)"action"))
259 						layoutreader_action_get(layout, curmod, &(mod->action), &(mod->argument));
260 				}
261 				mod_cb(mod, object, xkb);
262 				if (mod->action) xmlFree(mod->action);
263 				if (mod->argument) xmlFree(mod->argument);
264 			} else if (!xmlStrcmp(cur->name, (xmlChar *)"xpos")) {
265 				key->pos.x=layoutreader_double_get(layout->doc, cur);
266 			} else if (!xmlStrcmp(cur->name, (xmlChar *)"ypos")) {
267 				key->pos.y=layoutreader_double_get(layout->doc, cur);
268 			} else if (!xmlStrcmp(cur->name, (xmlChar *)"width")) {
269 				key->size.w=layoutreader_double_get(layout->doc, cur);
270 			} else if (!xmlStrcmp(cur->name, (xmlChar *)"height")) {
271 				key->size.h=layoutreader_double_get(layout->doc, cur);
272 			} else if (!xmlStrcmp(cur->name, (xmlChar *)"shape")) {
273 				key->shape=(char  *)xmlNodeListGetString(layout->doc, cur->children, 1);
274 			}
275 		}
276 		layoutreader_element_close(layout);
277 	} else layout->cur=cur;
278 	g_free(mod);
279 	END_FUNC
280 	return key;
281 }
282 
283 /* Free the 'key' element data */
284 void layoutreader_key_free(struct layout_key *key)
285 {
286 	START_FUNC
287 	if (key) {
288 		if (key->shape) xmlFree(key->shape);
289 		g_free(key);
290 	}
291 	END_FUNC
292 }
293 
294 /* Get the 'extension' element data (see keyboard.c) */
295 struct layout_extension *layoutreader_extension_new(struct layout *layout)
296 {
297 	START_FUNC
298 	struct layout_extension *extension=layoutreader_element_init(layout,
299 		"extension", sizeof(struct layout_extension));
300 	if (extension) for(;layout->cur && xmlStrcmp(layout->cur->name, (xmlChar *)"keyboard");
301 		layout->cur=layout->cur->next) {
302 		if (!xmlStrcmp(layout->cur->name, (xmlChar *)"name")) {
303 			layoutreader_update_lang(layout->doc, layout->cur->children, &extension->name);
304 		} else if (!xmlStrcmp(layout->cur->name, (xmlChar *)"placement")) {
305 			extension->placement=layoutreader_placement_get(layout);
306 		} else if (!xmlStrcmp(layout->cur->name, (xmlChar *)"identifiant")) {
307 			extension->identifiant=(char *)xmlNodeListGetString(layout->doc,
308 				layout->cur->children, 1);
309 		}
310 	}
311 	END_FUNC
312 	return extension;
313 }
314 
315 /* Free the 'extension' element data */
316 void layoutreader_extension_free(struct layout *layout, struct layout_extension *extension)
317 {
318 	START_FUNC
319 	layoutreader_element_close(layout);
320 	if (extension) {
321 		if (extension->name) xmlFree(extension->name);
322 		if (extension->identifiant) xmlFree(extension->identifiant);
323 		g_free(extension);
324 	}
325 	END_FUNC
326 }
327 
328 /* Read the trigger elements (only onhide for now) */
329 struct layout_trigger *layoutreader_trigger_new(struct layout *layout)
330 {
331 	START_FUNC
332 	unsigned char *action, *argument;
333 	GSList *list=layout->ids;
334 	xmlNodePtr cur=layout->cur;
335 	struct layout_trigger *trigger=layoutreader_element_init(layout,
336 		"onhide", sizeof(struct layout_trigger));
337 	if (trigger) {
338 		for(cur=layout->cur;cur;cur=cur->next) {
339 			if (!xmlStrcmp(cur->name, (xmlChar *)"action")) {
340 				layoutreader_action_get(layout, cur, &(action), &(argument));
341 				while (list && strcmp(((struct layout_id *)list->data)->name, (char *)argument)) {
342 					list=list->next;
343 				}
344 				if (list) trigger->object=((struct layout_id *)list->data)->object;
345 				else flo_error(_("Id %s was not found in layout file"), argument);
346 			}
347 		}
348 	} else layout->cur=cur;
349 	END_FUNC
350 	return trigger;
351 }
352 
353 /* Free the trigger data memory */
354 void layoutreader_trigger_free(struct layout *layout, struct layout_trigger *trigger)
355 {
356 	START_FUNC
357 	layoutreader_element_close(layout);
358 	if (trigger) g_free(trigger);
359 	END_FUNC
360 }
361 
362 /* Get the 'shape' element data (see style.c) */
363 struct layout_shape *layoutreader_shape_new(struct layout *layout)
364 {
365 	START_FUNC
366 	struct layout_shape *shape=layoutreader_element_init(layout,
367 		"shape", sizeof(struct layout_shape));
368 	xmlNodePtr cur=layout->cur;
369 	if (shape) for(;cur;cur=cur->next) {
370 		if (!xmlStrcmp(cur->name, (xmlChar *)"name")) {
371 			shape->name=(char *)xmlNodeListGetString(layout->doc, cur->children, 1);
372 		} else if (!xmlStrcmp(cur->name, (xmlChar *)"svg")) {
373 			shape->svg=layoutreader_svg_get(layout->doc, cur);
374 		}
375 	}
376 	layoutreader_element_close(layout);
377 	END_FUNC
378 	return shape;
379 }
380 
381 /* Free the 'shape' element data */
382 void layoutreader_shape_free(struct layout_shape *shape)
383 {
384 	START_FUNC
385 	if (shape) {
386 		if (shape->name) xmlFree(shape->name);
387 		if (shape->svg) g_free(shape->svg);
388 		g_free(shape);
389 	}
390 	END_FUNC
391 }
392 
393 /* Get the 'symbol' element data (see style.c) */
394 struct layout_symbol *layoutreader_symbol_new(struct layout *layout)
395 {
396 	START_FUNC
397 	struct layout_symbol *symbol=layoutreader_element_init(layout,
398 		"symbol", sizeof(struct layout_symbol));
399 	xmlNodePtr cur=layout->cur;
400 	if (symbol) for(;cur;cur=cur->next) {
401 		if (!xmlStrcmp(cur->name, (xmlChar *)"name")) {
402 			symbol->name=(char *)xmlNodeListGetString(layout->doc, cur->children, 1);
403 		} else if (!xmlStrcmp(cur->name, (xmlChar *)"svg")) {
404 			symbol->svg=layoutreader_svg_get(layout->doc, cur);
405 		} else if (!xmlStrcmp(cur->name, (xmlChar *)"label")) {
406 			layoutreader_update_lang(layout->doc, cur->children, &symbol->label);
407 		} else if (!xmlStrcmp(cur->name, (xmlChar *)"type")) {
408 			symbol->type=(char *)xmlNodeListGetString(layout->doc, cur->children, 1);
409 		}
410 	}
411 	layoutreader_element_close(layout);
412 	END_FUNC
413 	return symbol;
414 }
415 
416 /* Free the 'symbol' element data */
417 void layoutreader_symbol_free(struct layout_symbol *symbol)
418 {
419 	START_FUNC
420 	if (symbol) {
421 		if (symbol->name) xmlFree(symbol->name);
422 		if (symbol->svg) g_free(symbol->svg);
423 		if (symbol->label) xmlFree(symbol->label);
424 		if (symbol->type) xmlFree(symbol->type);
425 		g_free(symbol);
426 	}
427 	END_FUNC
428 }
429 
430 /* Get the 'sound' element data (see style.c) */
431 struct layout_sound *layoutreader_sound_new(struct layout *layout)
432 {
433 	START_FUNC
434 	struct layout_sound *sound=layoutreader_element_init(layout,
435 		"sound", sizeof(struct layout_sound));
436 	xmlNodePtr cur=layout->cur;
437 	xmlAttrPtr attr;
438 	if (sound) {
439 		attr=xmlHasProp(cur->parent, (xmlChar *)"match");
440 		if (attr) {
441 			sound->match=(char *)xmlNodeListGetString(layout->doc, attr->children, 1);
442 		}
443 		for(;cur;cur=cur->next) {
444 			if (!xmlStrcmp(cur->name, (xmlChar *)"press")) {
445 				sound->press=(char *)xmlNodeListGetString(layout->doc, cur->children, 1);
446 			} else if (!xmlStrcmp(cur->name, (xmlChar *)"release")) {
447 				sound->release=(char *)xmlNodeListGetString(layout->doc, cur->children, 1);
448 			} else if (!xmlStrcmp(cur->name, (xmlChar *)"hover")) {
449 				sound->hover=(char *)xmlNodeListGetString(layout->doc, cur->children, 1);
450 			}
451 		}
452 	}
453 	layoutreader_element_close(layout);
454 	END_FUNC
455 	return sound;
456 }
457 
458 /* Free the 'sound' element data */
459 void layoutreader_sound_free(struct layout_sound *sound)
460 {
461 	START_FUNC
462 	if (sound) {
463 		if (sound->match) xmlFree(sound->match);
464 		if (sound->press) xmlFree(sound->press);
465 		if (sound->release) xmlFree(sound->release);
466 		if (sound->hover) xmlFree(sound->hover);
467 		g_free(sound);
468 	}
469 	END_FUNC
470 }
471 
472 /* instanciates a new layout reader. Called in florence.c and style.c
473  * can be called either for layout or style files
474  * validates the xml layout against the relax-ng validation document
475  * return the layoutreader handle */
476 struct layout *layoutreader_new(char *layoutname, char *defaultname, char *relaxng)
477 {
478 	START_FUNC
479 	xmlRelaxNGParserCtxtPtr rngctx;
480 	xmlRelaxNGPtr rng;
481 	xmlRelaxNGValidCtxtPtr validrng;
482         gchar *layoutfile=NULL;
483         gchar *layoutdir=NULL;
484 	gchar *tmp=NULL;
485 	struct stat stat;
486 	int file_error=0;
487 	struct layout *layout=g_malloc(sizeof(struct layout));
488 	gboolean mustfree=FALSE;
489 
490 	memset(layout, 0, sizeof(struct layout));
491 	LIBXML_TEST_VERSION
492 
493 	layoutfile=layoutname;
494 	if ((!layoutfile) || (layoutfile[0]=='\0') || (file_error=lstat(layoutfile, &stat))) {
495 		if (file_error) flo_warn (_("Unable to open file %s, using default %s"),
496 			layoutfile, defaultname);
497 		layoutfile=(gchar *)defaultname;
498 	}
499 
500 	/* get basepath */
501 	lstat(layoutfile, &stat);
502 	if (S_ISDIR(stat.st_mode)) chdir(layoutfile);
503 	else if (strchr(layoutfile,'/')) {
504 		layoutdir=layoutfile;
505 		while (strchr(layoutdir+1,'/')) {
506 			layoutdir=strchr(layoutdir+1,'/');
507 		}
508 		layoutdir=g_strndup(layoutfile, layoutdir-layoutfile);
509 		chdir(layoutfile);
510 		g_free(layoutdir);
511 	}
512 
513 	/* if file is a directory, try to find a matching file in the directory */
514 	if (!lstat(layoutfile, &stat) && S_ISDIR(stat.st_mode)) {
515 		tmp=g_strdup_printf("%s/florence.style", layoutfile);
516 		if (lstat(tmp, &stat)) {
517 			g_free(tmp);
518 			tmp=g_strdup_printf("%s/florence.xml", layoutfile);
519 		}
520 		if (lstat(tmp, &stat)) {
521 			flo_error(_("%s is a directory and no matching file has been found inside"),
522 				layoutfile);
523 			g_free(tmp);
524 			g_free(layout);
525 			return NULL;
526 		}
527 		layoutfile=tmp; mustfree=TRUE;
528 	}
529 	layout->doc=xmlReadFile(layoutfile, NULL, XML_PARSE_NOENT|XML_PARSE_XINCLUDE);
530 	if (!layout->doc) flo_fatal (_("Unable to open file %s."), layoutfile);
531 	xmlXIncludeProcess(layout->doc);
532 
533 	rngctx=xmlRelaxNGNewParserCtxt(relaxng);
534 	rng=xmlRelaxNGParse(rngctx);
535 	validrng=xmlRelaxNGNewValidCtxt(rng);
536 	if (0!=xmlRelaxNGValidateDoc(validrng, layout->doc))
537 		flo_fatal(_("%s does not valdate against %s"), layoutfile, relaxng);
538 	flo_debug(TRACE_DEBUG, _("Using file %s"), layoutfile);
539 	if (mustfree) g_free(layoutfile);
540 
541 	xmlRelaxNGFreeValidCtxt(validrng);
542 	xmlRelaxNGFree(rng);
543 	xmlRelaxNGFreeParserCtxt(rngctx);
544 
545 	if (!layout->doc || !layout->doc->children)
546 		flo_error(_("File %s does not contain xml data"), layoutfile);
547 	else layout->cur=layout->doc->children;
548 	END_FUNC
549 	return layout;
550 }
551 
552 /* free the memory used by layout reader */
553 void layoutreader_free(struct layout *layout)
554 {
555 	START_FUNC
556 	GSList *list=layout->ids;
557 	while (list) {
558 		g_free(list->data);
559 		list=list->next;
560 	}
561 	g_slist_free(layout->ids);
562 	xmlFreeDoc(layout->doc);
563 	g_free(layout);
564 	END_FUNC
565 }
566 
567