1 /*
2  * DISTRHO Plugin Framework (DPF)
3  * Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com>
4  *
5  * Permission to use, copy, modify, and/or distribute this software for any purpose with
6  * or without fee is hereby granted, provided that the above copyright notice and this
7  * permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
10  * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
11  * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
12  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
13  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
14  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15  */
16 
17 #ifndef DISTRHO_EXTERNAL_WINDOW_HPP_INCLUDED
18 #define DISTRHO_EXTERNAL_WINDOW_HPP_INCLUDED
19 
20 #include "String.hpp"
21 
22 #ifdef DISTRHO_OS_WINDOWS
23 # error Unsupported platform!
24 #else
25 # include <cerrno>
26 # include <signal.h>
27 # include <sys/wait.h>
28 # include <unistd.h>
29 #endif
30 
31 START_NAMESPACE_DISTRHO
32 
33 // -----------------------------------------------------------------------
34 // ExternalWindow class
35 
36 class ExternalWindow
37 {
38 public:
ExternalWindow(const uint w=1,const uint h=1,const char * const t="")39     ExternalWindow(const uint w = 1, const uint h = 1, const char* const t = "")
40         : width(w),
41           height(h),
42           title(t),
43           transientWinId(0),
44           visible(false),
45           pid(0) {}
46 
~ExternalWindow()47     virtual ~ExternalWindow()
48     {
49         terminateAndWaitForProcess();
50     }
51 
getWidth() const52     uint getWidth() const noexcept
53     {
54         return width;
55     }
56 
getHeight() const57     uint getHeight() const noexcept
58     {
59         return height;
60     }
61 
getTitle() const62     const char* getTitle() const noexcept
63     {
64         return title;
65     }
66 
getTransientWinId() const67     uintptr_t getTransientWinId() const noexcept
68     {
69         return transientWinId;
70     }
71 
isVisible() const72     bool isVisible() const noexcept
73     {
74         return visible;
75     }
76 
isRunning()77     bool isRunning() noexcept
78     {
79         if (pid <= 0)
80             return false;
81 
82         const pid_t p = ::waitpid(pid, nullptr, WNOHANG);
83 
84         if (p == pid || (p == -1 && errno == ECHILD))
85         {
86             printf("NOTICE: Child process exited while idle\n");
87             pid = 0;
88             return false;
89         }
90 
91         return true;
92     }
93 
setSize(uint w,uint h)94     virtual void setSize(uint w, uint h)
95     {
96         width = w;
97         height = h;
98     }
99 
setTitle(const char * const t)100     virtual void setTitle(const char* const t)
101     {
102         title = t;
103     }
104 
setTransientWinId(const uintptr_t winId)105     virtual void setTransientWinId(const uintptr_t winId)
106     {
107         transientWinId = winId;
108     }
109 
setVisible(const bool yesNo)110     virtual void setVisible(const bool yesNo)
111     {
112         visible = yesNo;
113     }
114 
115 protected:
startExternalProcess(const char * args[])116     bool startExternalProcess(const char* args[])
117     {
118         terminateAndWaitForProcess();
119 
120         pid = vfork();
121 
122         switch (pid)
123         {
124         case 0:
125             execvp(args[0], (char**)args);
126             _exit(1);
127             return false;
128 
129         case -1:
130             printf("Could not start external ui\n");
131             return false;
132 
133         default:
134             return true;
135         }
136     }
137 
terminateAndWaitForProcess()138     void terminateAndWaitForProcess()
139     {
140         if (pid <= 0)
141             return;
142 
143         printf("Waiting for previous process to stop,,,\n");
144 
145         bool sendTerm = true;
146 
147         for (pid_t p;;)
148         {
149             p = ::waitpid(pid, nullptr, WNOHANG);
150 
151             switch (p)
152             {
153             case 0:
154                 if (sendTerm)
155                 {
156                     sendTerm = false;
157                     ::kill(pid, SIGTERM);
158                 }
159                 break;
160 
161             case -1:
162                 if (errno == ECHILD)
163                 {
164                     printf("Done! (no such process)\n");
165                     pid = 0;
166                     return;
167                 }
168                 break;
169 
170             default:
171                 if (p == pid)
172                 {
173                     printf("Done! (clean wait)\n");
174                     pid = 0;
175                     return;
176                 }
177                 break;
178             }
179 
180             // 5 msec
181             usleep(5*1000);
182         }
183     }
184 
185 private:
186     uint width;
187     uint height;
188     String title;
189     uintptr_t transientWinId;
190     bool visible;
191     pid_t pid;
192 
193     friend class UIExporter;
194 
195     DISTRHO_DECLARE_NON_COPY_CLASS(ExternalWindow)
196 };
197 
198 // -----------------------------------------------------------------------
199 
200 END_NAMESPACE_DISTRHO
201 
202 #endif // DISTRHO_EXTERNAL_WINDOW_HPP_INCLUDED
203