Fix bugs in previous commit that caused FTBFS in synfig and ETL FTBFS with older...
[synfig.git] / synfig-studio / tags / 0.61.08 / 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 #include "general.h"
71
72 #endif
73
74 /* === U S I N G =========================================================== */
75
76 using namespace std;
77 using namespace etl;
78 using namespace synfig;
79 using namespace studio;
80
81 /* === M A C R O S ========================================================= */
82
83 /* === G L O B A L S ======================================================= */
84
85 #ifdef _WIN32
86 #define WIN32_PIPE_PATH "\\\\.\\pipe\\SynfigStudio.Cmd"
87 static synfig::Mutex cmd_mutex;
88 static std::list<synfig::String> cmd_queue;
89 static Glib::Dispatcher* cmd_dispatcher;
90 static void
91 pipe_listen_thread()
92 {
93         for(;;)
94         {
95                 HANDLE pipe_handle;
96                 pipe_handle=CreateNamedPipe(
97                         WIN32_PIPE_PATH, // pipe name
98                         PIPE_ACCESS_INBOUND, // Access type
99                         PIPE_READMODE_BYTE /*|PIPE_NOWAIT*/,
100                         PIPE_UNLIMITED_INSTANCES,
101                         BUFSIZE,
102                         BUFSIZE,
103                         NMPWAIT_USE_DEFAULT_WAIT,
104                         NULL
105                 );
106                 if(pipe_handle==INVALID_HANDLE_VALUE)
107                 {
108                         synfig::error("IPC(): Call to CreateNamedPipe failed. Ignore next error. GetLastError=%d",GetLastError());
109                         return;
110                 }
111
112                 bool connected;
113                 connected=ConnectNamedPipe(pipe_handle,NULL)?true:(GetLastError()==ERROR_PIPE_CONNECTED);
114                 DWORD read_bytes;
115                 bool success;
116
117                 Glib::Thread::yield();
118
119                 if(connected)
120                 do {
121                         String data;
122                         char c;
123                         do
124                         {
125                                 success= ReadFile(
126                                         pipe_handle,
127                                         &c,             // buffer pointer
128                                         1,              // buffer size
129                                         &read_bytes,
130                                         NULL
131                                 );
132                                 if(success && read_bytes==1 && c!='\n')
133                                         data+=c;
134                         }while(c!='\n');
135                         synfig::Mutex::Lock lock(cmd_mutex);
136                         cmd_queue.push_back(data);
137                         cmd_dispatcher->emit();
138                 } while(success && read_bytes);
139
140                 CloseHandle(pipe_handle);
141         }
142 }
143
144 static void
145 empty_cmd_queue()
146 {
147         synfig::Mutex::Lock lock(cmd_mutex);
148         while(!cmd_queue.empty())
149         {
150                 IPC::process_command(cmd_queue.front());
151                 cmd_queue.pop_front();
152         }
153 }
154
155 #endif
156
157 /* === P R O C E D U R E S ================================================= */
158
159 /* === M E T H O D S ======================================================= */
160
161 IPC::IPC()
162 {
163 #ifdef _WIN32
164
165         cmd_dispatcher=new Glib::Dispatcher;
166         cmd_dispatcher->connect(sigc::ptr_fun(empty_cmd_queue));
167
168         Glib::Thread::create(
169                 sigc::ptr_fun(pipe_listen_thread),
170                 false
171         );
172
173 #else
174
175         remove(fifo_path().c_str());
176         fd=-1;
177
178         if(mkfifo(fifo_path().c_str(), S_IRWXU)!=0)
179         {
180                 synfig::error("IPC(): mkfifo failed for "+fifo_path());
181         }
182
183         {
184                 fd=open(fifo_path().c_str(),O_RDWR);
185
186
187                 if(fd<0)
188                 {
189                         synfig::error("IPC(): Failed to open fifo \"%s\". (errno=?)",fifo_path().c_str());
190                         //synfig::error("IPC(): Failed to open fifo \"%s\". (errno=%d)",fifo_path().c_str(),::errno);
191                 }
192                 else
193                 {
194                         file=SmartFILE(fdopen(fd,"r"));
195
196                         Glib::signal_io().connect(
197                                 sigc::mem_fun(this,&IPC::fifo_activity),
198                                 fd,
199                                 Glib::IO_IN|Glib::IO_PRI|Glib::IO_ERR|Glib::IO_HUP|Glib::IO_NVAL
200                         );
201                 }
202         }
203 #endif
204 }
205
206 IPC::~IPC()
207 {
208         //if(file)
209         //      fclose(file.get());
210
211         remove(fifo_path().c_str());
212
213         //if(fd>=0)
214         //      close(fd);
215 }
216
217 synfig::String
218 IPC::fifo_path()
219 {
220 #ifdef _WIN32
221         return WIN32_PIPE_PATH;
222 #else
223         return Glib::build_filename(App::get_user_app_directory(),"fifo");
224 #endif
225 }
226
227 bool
228 IPC::fifo_activity(Glib::IOCondition cond)
229 {
230         synfig::info(__FILE__":%d: fifo activity",__LINE__);
231
232         if(cond&(Glib::IO_ERR|Glib::IO_HUP|Glib::IO_NVAL))
233         {
234                 if(cond&(Glib::IO_ERR))
235                         synfig::error("IPC::fifo_activity(): IO_ERR");
236                 if(cond&(Glib::IO_HUP))
237                         synfig::error("IPC::fifo_activity(): IO_HUP");
238                 if(cond&(Glib::IO_NVAL))
239                         synfig::error("IPC::fifo_activity(): IO_NVAL");
240                 return false;
241         }
242         synfig::info(__FILE__":%d: fifo activity",__LINE__);
243
244         String command;
245         {
246                 char tmp;
247                 do {
248                         if(read(fd,&tmp,sizeof(tmp))<=0)
249                                 break;
250                         if(tmp!='\n')
251                                 command+=tmp;
252                 } while(tmp!='\n');
253         }
254
255         process_command(command);
256         return true;
257 }
258
259 bool
260 IPC::process_command(const synfig::String& command_line)
261 {
262         if(command_line.empty())
263                 return false;
264
265         char cmd = command_line[0];
266
267         String args(command_line.begin()+1,command_line.end());
268
269         // erase leading spaces
270         while (!args.empty() && args[0] == ' ')
271                 args.erase(args.begin());
272
273         // erase trailing newlines and spaces
274         while (!args.empty() && (args[args.size()-1] == '\n' || args[args.size()-1] == ' '))
275                 args.erase(args.end()-1);
276
277         switch(toupper(cmd))
278         {
279                 case 'F': // Focus/Foreground
280                         App::signal_present_all()();
281                         break;
282                 case 'N': // New file
283                         App::signal_present_all()();
284                         App::new_instance();
285                         break;
286                 case 'O': // Open <arg>
287                         App::signal_present_all()();
288                         App::open(args);
289                         break;
290                 case 'X': // Quit
291                 case 'Q': // Quit
292                         App::quit();
293                         break;
294                 default:
295                         synfig::warning("Received unknown command '%c' with arg '%s'",cmd,args.c_str());
296                         break;
297         }
298
299         return true;
300 }
301
302 synfig::SmartFILE
303 IPC::make_connection()
304 {
305         SmartFILE ret;
306 #ifdef _WIN32
307         HANDLE pipe_handle;
308         pipe_handle=CreateFile(
309                 fifo_path().c_str(),
310                 GENERIC_WRITE, // desired access
311                 0, // share mode
312                 NULL, // security attributes
313                 OPEN_EXISTING, // creation disposition
314                 FILE_ATTRIBUTE_NORMAL, // flags and attributes
315                 NULL  // template file
316         );
317         if(pipe_handle==INVALID_HANDLE_VALUE)
318         {
319                 DWORD error = GetLastError();
320 #ifndef _DEBUG
321                 if( error != ERROR_FILE_NOT_FOUND )
322 #endif
323                         synfig::warning("IPC::make_connection(): Unable to connect to previous instance. GetLastError=%d",error);
324         }
325         int fd=_open_osfhandle(reinterpret_cast<long int>(pipe_handle),_O_APPEND|O_WRONLY);
326 #else
327         struct stat file_stat;
328         if(stat(fifo_path().c_str(),&file_stat)!=0)
329                 return ret;
330
331         if(!S_ISFIFO(file_stat.st_mode))
332                 return ret;
333
334         int fd=open(fifo_path().c_str(),O_WRONLY|O_NONBLOCK);
335 #endif
336
337         if(fd>=0)
338                 ret=SmartFILE(fdopen(fd,"w"));
339
340 #ifdef _DEBUG
341         // synfig::info("uplink fd=%d",fd);
342 #endif
343
344         return ret;
345 }