Add Russian translation provided by Валерий Крувялис <valkru@mail.ru>
[xiph-mirror.git] / sushivision / toplevel.c
blobfb79e6d323f1391ccea9bda41208497beaceacb5
1 /*
3 * sushivision copyright (C) 2006-2007 Monty <monty@xiph.org>
5 * sushivision is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
8 * any later version.
9 *
10 * sushivision is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with sushivision; see the file COPYING. If not, write to the
17 * Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
22 #define _GNU_SOURCE
23 #include <string.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <unistd.h>
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <fcntl.h>
30 #include <errno.h>
31 #include <math.h>
32 #include <signal.h>
33 #include <sys/stat.h>
34 #include <sys/types.h>
35 #include <gtk/gtk.h>
36 #include <cairo-ft.h>
37 #include <pthread.h>
38 #include <dlfcn.h>
39 #include "internal.h"
40 #include "sushi-gtkrc.h"
42 // All API and GTK/GDK access is locked by a single recursive mutex
43 // installed into GDK.
45 // Worker threads exist to handle high latency tasks asynchronously.
46 // The worker threads (with one exception) never touch the main GDK
47 // mutex; any data protected by the main mutex needed for a worker
48 // task is pulled out and encapsulated when that task is set up from a
49 // GTK or API call. The one exception to worker threads and the GDK
50 // lock is expose requests after rendering; in this case, the worker
51 // waits until it can drop all locks, does so, and then issues an
52 // expose request locked by GDK.
54 // All data object memory strutures that are used by the worker
55 // threads and can be manipulated by API/GTK/GDK and the worker
56 // threads re locked by r/w locks; any thread 'inside' the abstraction
57 // read-locks the memory while it is in use. GDK/API access
58 // write-locks this memory to manipulate it (allocation, deallocation,
59 // structural mutation).
61 // lock acquisition order must move to the right:
62 // GDK -> panel_m -> status_m -> plot_m
64 // mutex condm is only for protecting the worker condvar
65 static pthread_mutex_t worker_condm = PTHREAD_MUTEX_INITIALIZER;
66 static pthread_cond_t worker_cond = PTHREAD_COND_INITIALIZER;
67 static pthread_cond_t idle_cond = PTHREAD_COND_INITIALIZER;
68 static sig_atomic_t _sv_exiting=0;
69 static sig_atomic_t _sv_running=0;
70 static int wake_pending = 0;
71 static int idling = 0;
72 static int num_threads=0;
74 static int num_proccies(){
75 FILE *f = fopen("/proc/cpuinfo","r");
76 char * line = NULL;
77 size_t len = 0;
78 ssize_t read;
79 int num=1,arg;
81 if (f == NULL) return 1;
82 while ((read = getline(&line, &len, f)) != -1) {
83 if(sscanf(line,"processor : %d",&arg)==1)
84 if(arg+1>num)num=arg+1;
86 if (line)
87 free(line);
89 fprintf(stderr,"Number of processors: %d\n",num);
90 fclose(f);
91 return num;
94 static void *worker_thread(void *dummy){
95 while(1){
96 int i,flag=0;
97 if(_sv_exiting)break;
98 if(_sv_running){
99 pthread_rwlock_rdlock(panellist_m);
100 for(i=0;i<_sv_panels;i++){
101 sv_panel_t *p = _sv_panel_list[i];
102 int ret;
104 if(_sv_exiting)break;
105 if(!_sv_running)break;
107 if(p){
108 _sv_spinner_set_busy(p->spinner);
109 ret = _sv_panel_work(p);
110 if(ret == STATUS_WORKING){
111 flag = 1;
112 sv_wake(); // result of this completion might have
113 // generated more work
115 if(ret == STATUS_IDLE)
116 _sv_spinner_set_idle(p->spinner);
120 if(flag==1)continue;
122 // nothing to do, wait
123 pthread_mutex_lock(&worker_condm);
124 idling++;
125 pthread_cond_signal(&idle_cond);
126 while(!wake_pending)
127 pthread_cond_wait(&worker_cond,&worker_condm);
129 idling--;
130 wake_pending--;
131 pthread_mutex_unlock(&worker_condm);
134 pthread_mutex_unlock(&worker_condm);
135 return 0;
138 static char *gtkrc_string(){
139 return _SUSHI_GTKRC_STRING;
142 char *_sv_appname = NULL;
143 char *_sv_filename = NULL;
144 char *_sv_filebase = NULL;
145 char *_sv_dirname = NULL;
146 char *_sv_cwdname = NULL;
148 static void *eventloop(void *dummy){
150 gdk_lock();
151 gtk_main ();
152 gdk_unlock();
154 // in case there's another mainloop in the main app
155 gdk_lock();
156 if(!gtk_main_iteration_do(FALSE)) // side effect: returns true if
157 // there are no main loops active
158 gtk_main_quit();
159 gdk_unlock();
161 return 0;
164 /* externally visible interface */
165 int sv_init(void){
166 int ret=0;
167 if((ret=pthread_key_create(&_sv_dim_key,NULL)))
168 return ret;
169 if((ret=pthread_key_create(&_sv_obj_key,NULL)))
170 return ret;
171 if((ret=pthread_key_create(&_sv_panel_key,NULL)))
172 return ret;
174 num_threads = ((num_proccies()*3)>>2);
176 _gtk_mutex_fixup();
177 gtk_init (NULL,NULL);
179 _sv_appname = g_get_prgname ();
180 _sv_cwdname = getcwd(NULL,0);
181 _sv_dirname = strdup(_sv_cwdname);
183 if(_sv_appname){
184 char *base = strrchr(_sv_appname,'/');
185 if(!base)
186 base = _sv_appname;
187 else
188 base++;
190 asprintf(&_sv_filebase, "%s.sushi",base);
191 }else
192 _sv_filebase = strdup("unnamed.sushi");
193 asprintf(&_sv_filename,"%s/%s",_sv_dirname,_sv_filebase);
195 if (!g_thread_supported ()) g_thread_init (NULL);
196 gdk_threads_init ();
198 gtk_rc_parse_string(gtkrc_string());
199 gtk_rc_add_default_file("sushi-gtkrc");
201 _gtk_button3_fixup();
203 // worker threads
205 pthread_t dummy;
206 int threads = num_threads;
207 while(threads--)
208 pthread_create(&dummy, NULL, &worker_thread,NULL);
211 // eventloop for panels (in the event we're injected into an app
212 // with no gtk main loop; multiple such loops can coexist)
214 pthread_t dummy;
215 return pthread_create(&dummy, NULL, &event_thread,NULL);
218 return 0;
221 int sv_join(void){
222 while(!_sv_exiting){
223 pthread_mutex_lock(&worker_condm);
224 pthread_cond_wait(&worker_cond,&worker_condm);
225 pthread_mutex_unlock(&worker_condm);
227 return 0;
230 int sv_wake(void){
231 if(_sv_running){
232 pthread_mutex_lock(&worker_condm);
233 wake_pending = num_threads;
234 pthread_cond_broadcast(&worker_cond);
235 pthread_mutex_unlock(&worker_condm);
237 return 0;
240 int sv_exit(void){
241 _sv_exiting = 1;
242 _sv_running = 1;
243 sv_wake();
245 gdk_threads_enter();
246 if(!gtk_main_iteration_do(FALSE)) // side effect: returns true if
247 // there are no main loops active
248 gtk_main_quit();
250 gdk_threads_leave();
251 return 0;
254 int sv_suspend(int block){
255 _sv_running=0;
256 if(block){
257 // block until all worker threads idle
258 while(idling < num_threads){
259 pthread_mutex_lock(&worker_condm);
260 pthread_cond_wait(&idle_cond,&worker_condm);
261 pthread_mutex_unlock(&worker_condm);
264 return 0;
267 int sv_resume(void){
268 _sv_running=1;
269 sv_wake();
272 void _sv_first_load_warning(int *warn){
273 if(!*warn)
274 fprintf(stderr,"\nWARNING: The data file to be opened is not a perfect match to\n"
275 "%s.\n\nThis may be because the file is for a different version of\n"
276 "the executable, or because it is is not a save file for \n%s at all.\n\n"
277 "Specific warnings follow:\n\n",_sv_appname,_sv_appname);
278 *warn = 1;
281 static void set_internal_filename(char *filename){
282 // save the filename for internal menu seeding purposes
283 char *base = strrchr(filename,'/');
285 // filename may include a path; pull it off and apply it toward dirname
286 if(base){
287 base[0] = '\0';
288 char *dirbit = strdup(_sv_filebase);
289 _sv_filebase = base+1;
290 if(g_path_is_absolute(dirbit)){
291 // replace dirname
292 free(_sv_dirname);
293 _sv_dirname = dirbit;
294 }else{
295 // append to dirname
296 char *buf;
297 asprintf(&buf,"%s/%s",_sv_dirname,dirbit);
298 free(_sv_dirname);
299 _sv_dirname = buf;
302 asprintf(&_sv_filename,"%s/%s",_sv_dirname,_sv_filebase);
305 int sv_save(char *filename){
306 int i, ret=0;
307 int fd;
309 LIBXML_TEST_VERSION;
311 fd = open(_sv_filename, O_RDWR|O_CREAT, 0660);
312 if(fd<0){
313 ret = 1;
314 goto done;
317 gdk_threads_enter();
319 xmlSaveCtxtPtr xmlptr =
320 xmlSaveToFd (fd, "UTF-8", XML_SAVE_FORMAT);
322 xmlDocPtr doc = xmlNewDoc((xmlChar *)"1.0");
323 xmlNodePtr root_node = xmlNewNode(NULL, (xmlChar *)_sv_appname);
324 xmlDocSetRootElement(doc, root_node);
326 // dimension values are independent of panel
327 for(i=0;i<_sv_dimensions;i++)
328 ret|=_sv_dim_save(_sv_dimension_list[i], root_node);
330 // objectives have no independent settings
332 // panel settings (by panel)
333 for(i=0;i<_sv_panels;i++)
334 ret|=_sv_panel_save(_sv_panel_list[i], root_node);
336 if(xmlSaveDoc(xmlptr,doc)<0)
337 ret=1;
339 if(xmlSaveClose(xmlptr)<0)
340 ret=1;
342 close(fd);
344 if(ret==0) set_internal_filename(filename);
346 xmlFreeDoc(doc);
347 xmlCleanupParser();
349 gdk_threads_leave();
350 done:
351 return ret;
354 int sv_load(filename){
355 xmlDoc *doc = NULL;
356 xmlNode *root = NULL;
357 int fd,warn=0;
358 int i;
360 LIBXML_TEST_VERSION;
362 fd = open(_sv_filename, O_RDONLY);
363 if(fd<0) return 1;
365 doc = xmlReadFd(fd, NULL, NULL, 0);
366 close(fd);
368 if (doc == NULL) {
369 errno = -EINVAL;
370 return 1;
373 root = xmlDocGetRootElement(doc);
375 // piggyback off undo (as it already goes through the trouble of
376 // doing correct unrolling, which can be tricky)
378 // if this instance has an undo stack, pop it all, then log current state into it
379 gdk_threads_enter();
380 _sv_undo_level=0;
381 _sv_undo_log();
383 _sv_undo_t *u = _sv_undo_stack[_sv_undo_level];
385 // load dimensions
386 for(i=0;i<_sv_dimensions;i++){
387 sv_dim_t *d = _sv_dimension_list[i];
388 if(d){
389 xmlNodePtr dn = _xmlGetChildI(root,"dimension","number",d->number);
390 if(!dn){
391 _sv_first_load_warning(&warn);
392 fprintf(stderr,"Could not find data for dimension \"%s\" in save file.\n",
393 d->name);
394 }else{
395 warn |= _sv_dim_load(d,u,dn,warn);
396 xmlFreeNode(dn);
401 // load panels
402 for(i=0;i<_sv_panels;i++){
403 sv_panel_t *p = _sv_panel_list[i];
404 if(p){
405 xmlNodePtr pn = _xmlGetChildI(root,"panel","number",p->number);
406 if(!pn){
407 _sv_first_load_warning(&warn);
408 fprintf(stderr,"Could not find data for panel \"%s\" in save file.\n",
409 p->name);
410 }else{
411 warn |= _sv_panel_load(p,u->panels+i,pn,warn);
412 xmlFreeNode(pn);
417 // if any elements are unclaimed, warn
418 xmlNodePtr node = root->xmlChildrenNode;
419 while(node){
420 if (node->type == XML_ELEMENT_NODE) {
421 xmlChar *name = xmlGetProp(node, (xmlChar *)"name");
422 _sv_first_load_warning(&warn);
423 if(name){
424 fprintf(stderr,"Save file contains data for nonexistant object \"%s\".\n",
425 name);
426 xmlFree(name);
427 }else
428 fprintf(stderr,"Save file root node contains an extra unknown elements.\n");
430 node = node->next;
433 if(ret==0) set_internal_filename(filename);
435 // effect the loaded values
436 _sv_undo_suspend();
437 _sv_undo_restore();
438 _sv_undo_resume();
439 gdk_threads_leave();
441 xmlFreeDoc(doc);
442 xmlCleanupParser();
445 return 0;