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