Fix bugs in previous commit that caused FTBFS in synfig and ETL FTBFS with older...
[synfig.git] / synfig-studio / tags / 0.61.09 / 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         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
241         String command;
242         {
243                 char tmp;
244                 do {
245                         if(read(fd,&tmp,sizeof(tmp))<=0)
246                                 break;
247                         if(tmp!='\n')
248                                 command+=tmp;
249                 } while(tmp!='\n');
250         }
251
252         synfig::info("%s:%d: fifo activity: '%s'", __FILE__, __LINE__, command.c_str());
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
267         // erase leading spaces
268         while (!args.empty() && args[0] == ' ')
269                 args.erase(args.begin());
270
271         // erase trailing newlines and spaces
272         while (!args.empty() && (args[args.size()-1] == '\n' || args[args.size()-1] == ' '))
273                 args.erase(args.end()-1);
274
275         switch(toupper(cmd))
276         {
277                 case 'F': // Focus/Foreground
278                         App::signal_present_all()();
279                         break;
280                 case 'N': // New file
281                         App::signal_present_all()();
282                         App::new_instance();
283                         break;
284                 case 'O': // Open <arg>
285                         App::signal_present_all()();
286                         App::open(args);
287                         break;
288                 case 'X': // Quit
289                 case 'Q': // Quit
290                         App::quit();
291                         break;
292                 default:
293                         synfig::warning("Received unknown command '%c' with arg '%s'",cmd,args.c_str());
294                         break;
295         }
296
297         return true;
298 }
299
300 synfig::SmartFILE
301 IPC::make_connection()
302 {
303         SmartFILE ret;
304 #ifdef _WIN32
305         HANDLE pipe_handle;
306         pipe_handle=CreateFile(
307                 fifo_path().c_str(),
308                 GENERIC_WRITE, // desired access
309                 0, // share mode
310                 NULL, // security attributes
311                 OPEN_EXISTING, // creation disposition
312                 FILE_ATTRIBUTE_NORMAL, // flags and attributes
313                 NULL  // template file
314         );
315         if(pipe_handle==INVALID_HANDLE_VALUE)
316         {
317                 DWORD error = GetLastError();
318 #ifndef _DEBUG
319                 if( error != ERROR_FILE_NOT_FOUND )
320 #endif
321                         synfig::warning("IPC::make_connection(): Unable to connect to previous instance. GetLastError=%d",error);
322         }
323         int fd=_open_osfhandle(reinterpret_cast<long int>(pipe_handle),_O_APPEND|O_WRONLY);
324 #else
325         struct stat file_stat;
326         if(stat(fifo_path().c_str(),&file_stat)!=0)
327                 return ret;
328
329         if(!S_ISFIFO(file_stat.st_mode))
330                 return ret;
331
332         int fd=open(fifo_path().c_str(),O_WRONLY|O_NONBLOCK);
333 #endif
334
335         if(fd>=0)
336                 ret=SmartFILE(fdopen(fd,"w"));
337
338 #ifdef _DEBUG
339         // synfig::info("uplink fd=%d",fd);
340 #endif
341
342         return ret;
343 }