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