Fix a crash when running single-threaded and dragging the time slider.
[synfig.git] / synfig-studio / trunk / src / gtkmm / ipc.cpp
1 /* === S Y N F I G ========================================================= */
2 /*!     \file ipc.cpp
3 **      \brief Template File
4 **
5 **      $Id$
6 **
7 **      \legal
8 **      Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
9 **      Copyright (c) 2007 Chris Moore
10 **
11 **      This package is free software; you can redistribute it and/or
12 **      modify it under the terms of the GNU General Public License as
13 **      published by the Free Software Foundation; either version 2 of
14 **      the License, or (at your option) any later version.
15 **
16 **      This package is distributed in the hope that it will be useful,
17 **      but WITHOUT ANY WARRANTY; without even the implied warranty of
18 **      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 **      General Public License for more details.
20 **      \endlegal
21 */
22 /* ========================================================================= */
23
24 /* === H E A D E R S ======================================================= */
25
26 #ifdef USING_PCH
27 #       include "pch.h"
28 #else
29 #ifdef HAVE_CONFIG_H
30 #       include <config.h>
31 #endif
32
33 #include "ipc.h"
34
35 #ifdef HAVE_SYS_TYPES_H
36 #include <sys/types.h>
37 #endif
38
39 #ifdef HAVE_SYS_STAT_H
40 #include <sys/stat.h>
41 #endif
42
43 #ifdef HAVE_SYS_ERRNO_H
44 #include <sys/errno.h>
45 #endif
46
47 #include <synfig/main.h>
48 #include "app.h"
49
50 #ifdef HAVE_UNISTD_H
51 #include <unistd.h>
52 #endif
53
54 #ifdef HAVE_FCNTL_H
55 #include <fcntl.h>
56 #endif
57
58 #ifdef _WIN32
59 #include <windows.h>
60 #define BUFSIZE   128
61 #define read    _read
62 #endif
63
64 #include "toolbox.h"
65 #include <glibmm/dispatcher.h>
66 #include <synfig/mutex.h>
67 #include <synfig/string.h>
68 #include <glibmm/thread.h>
69
70 #endif
71
72 /* === U S I N G =========================================================== */
73
74 using namespace std;
75 using namespace etl;
76 using namespace synfig;
77 using namespace studio;
78
79 /* === M A C R O S ========================================================= */
80
81 /* === G L O B A L S ======================================================= */
82
83 #ifdef _WIN32
84 #define WIN32_PIPE_PATH "\\\\.\\pipe\\SynfigStudio.Cmd"
85 static synfig::Mutex cmd_mutex;
86 static std::list<synfig::String> cmd_queue;
87 static Glib::Dispatcher* cmd_dispatcher;
88 static void
89 pipe_listen_thread()
90 {
91         for(;;)
92         {
93                 HANDLE pipe_handle;
94                 pipe_handle=CreateNamedPipe(
95                         WIN32_PIPE_PATH, // pipe name
96                         PIPE_ACCESS_INBOUND, // Access type
97                         PIPE_READMODE_BYTE /*|PIPE_NOWAIT*/,
98                         PIPE_UNLIMITED_INSTANCES,
99                         BUFSIZE,
100                         BUFSIZE,
101                         NMPWAIT_USE_DEFAULT_WAIT,
102                         NULL
103                 );
104                 if(pipe_handle==INVALID_HANDLE_VALUE)
105                 {
106                         synfig::error("IPC(): Call to CreateNamedPipe failed. Ignore next error. GetLastError=%d",GetLastError());
107                         return;
108                 }
109
110                 bool connected;
111                 connected=ConnectNamedPipe(pipe_handle,NULL)?true:(GetLastError()==ERROR_PIPE_CONNECTED);
112                 DWORD read_bytes;
113                 bool success;
114
115                 Glib::Thread::yield();
116
117                 if(connected)
118                 do {
119                         String data;
120                         char c;
121                         do
122                         {
123                                 success= ReadFile(
124                                         pipe_handle,
125                                         &c,             // buffer pointer
126                                         1,              // buffer size
127                                         &read_bytes,
128                                         NULL
129                                 );
130                                 if(success && read_bytes==1 && c!='\n')
131                                         data+=c;
132                         }while(c!='\n');
133                         synfig::Mutex::Lock lock(cmd_mutex);
134                         cmd_queue.push_back(data);
135                         cmd_dispatcher->emit();
136                 } while(success && read_bytes);
137
138                 CloseHandle(pipe_handle);
139         }
140 }
141
142 static void
143 empty_cmd_queue()
144 {
145         synfig::Mutex::Lock lock(cmd_mutex);
146         while(!cmd_queue.empty())
147         {
148                 IPC::process_command(cmd_queue.front());
149                 cmd_queue.pop_front();
150         }
151 }
152
153 #endif
154
155 /* === P R O C E D U R E S ================================================= */
156
157 /* === M E T H O D S ======================================================= */
158
159 IPC::IPC()
160 {
161 #ifdef _WIN32
162
163         cmd_dispatcher=new Glib::Dispatcher;
164         cmd_dispatcher->connect(sigc::ptr_fun(empty_cmd_queue));
165
166         Glib::Thread::create(
167                 sigc::ptr_fun(pipe_listen_thread),
168                 false
169         );
170
171 #else
172
173         remove(fifo_path().c_str());
174         fd=-1;
175
176         if(mkfifo(fifo_path().c_str(), S_IRWXU)!=0)
177         {
178                 synfig::error("IPC(): mkfifo failed for "+fifo_path());
179         }
180
181         {
182                 fd=open(fifo_path().c_str(),O_RDWR);
183
184
185                 if(fd<0)
186                 {
187                         synfig::error("IPC(): Failed to open fifo \"%s\". (errno=?)",fifo_path().c_str());
188                         //synfig::error("IPC(): Failed to open fifo \"%s\". (errno=%d)",fifo_path().c_str(),::errno);
189                 }
190                 else
191                 {
192                         file=SmartFILE(fdopen(fd,"r"));
193
194                         Glib::signal_io().connect(
195                                 sigc::mem_fun(this,&IPC::fifo_activity),
196                                 fd,
197                                 Glib::IO_IN|Glib::IO_PRI|Glib::IO_ERR|Glib::IO_HUP|Glib::IO_NVAL
198                         );
199                 }
200         }
201 #endif
202 }
203
204 IPC::~IPC()
205 {
206         //if(file)
207         //      fclose(file.get());
208
209         remove(fifo_path().c_str());
210
211         //if(fd>=0)
212         //      close(fd);
213 }
214
215 synfig::String
216 IPC::fifo_path()
217 {
218 #ifdef _WIN32
219         return WIN32_PIPE_PATH;
220 #else
221         return Glib::build_filename(App::get_user_app_directory(),"fifo");
222 #endif
223 }
224
225 bool
226 IPC::fifo_activity(Glib::IOCondition cond)
227 {
228         synfig::info(__FILE__":%d: fifo activity",__LINE__);
229
230         if(cond&(Glib::IO_ERR|Glib::IO_HUP|Glib::IO_NVAL))
231         {
232                 if(cond&(Glib::IO_ERR))
233                         synfig::error("IPC::fifo_activity(): IO_ERR");
234                 if(cond&(Glib::IO_HUP))
235                         synfig::error("IPC::fifo_activity(): IO_HUP");
236                 if(cond&(Glib::IO_NVAL))
237                         synfig::error("IPC::fifo_activity(): IO_NVAL");
238                 return false;
239         }
240         synfig::info(__FILE__":%d: fifo activity",__LINE__);
241
242         String command;
243         {
244                 char tmp;
245                 do {
246                         if(read(fd,&tmp,sizeof(tmp))<=0)
247                                 break;
248                         if(tmp!='\n')
249                                 command+=tmp;
250                 } while(tmp!='\n');
251         }
252
253         process_command(command);
254         return true;
255 }
256
257 bool
258 IPC::process_command(const synfig::String& command_line)
259 {
260         if(command_line.empty())
261                 return false;
262
263         char cmd=command_line[0];
264
265         String args(command_line.begin()+1,command_line.end());
266         while(!args.empty() && args[0]==' ') args.erase(args.begin());
267         while(!args.empty() && args[args.size()-1]=='\n' || args[args.size()-1]==' ') args.erase(args.end()-1);
268
269         switch(toupper(cmd))
270         {
271                 case 'F': // Focus/Foreground
272                         App::signal_present_all()();
273                         break;
274                 case 'N': // New file
275                         App::signal_present_all()();
276                         App::new_instance();
277                         break;
278                 case 'O': // Open <arg>
279                         App::signal_present_all()();
280                         App::open(args);
281                         break;
282                 case 'X': // Quit
283                 case 'Q': // Quit
284                         App::quit();
285                         break;
286                 default:
287                         synfig::warning("Received unknown command '%c' with arg '%s'",cmd,args.c_str());
288                         break;
289         }
290
291         return true;
292 }
293
294 synfig::SmartFILE
295 IPC::make_connection()
296 {
297         SmartFILE ret;
298 #ifdef _WIN32
299         HANDLE pipe_handle;
300         pipe_handle=CreateFile(
301                 fifo_path().c_str(),
302                 GENERIC_WRITE, // desired access
303                 0, // share mode
304                 NULL, // security attributes
305                 OPEN_EXISTING, // creation disposition
306                 FILE_ATTRIBUTE_NORMAL, // flags and attributes
307                 NULL  // template file
308         );
309         if(pipe_handle==INVALID_HANDLE_VALUE)
310         {
311                 DWORD error = GetLastError();
312 #ifndef _DEBUG
313                 if( error != ERROR_FILE_NOT_FOUND )
314 #endif
315                         synfig::warning("IPC::make_connection(): Unable to connect to previous instance. GetLastError=%d",error);
316         }
317         int fd=_open_osfhandle(reinterpret_cast<long int>(pipe_handle),_O_APPEND|O_WRONLY);
318 #else
319         struct stat file_stat;
320         if(stat(fifo_path().c_str(),&file_stat)!=0)
321                 return ret;
322
323         if(!S_ISFIFO(file_stat.st_mode))
324                 return ret;
325
326         int fd=open(fifo_path().c_str(),O_WRONLY|O_NONBLOCK);
327 #endif
328
329         if(fd>=0)
330                 ret=SmartFILE(fdopen(fd,"w"));
331
332 #ifdef _DEBUG
333         // synfig::info("uplink fd=%d",fd);
334 #endif
335
336         return ret;
337 }