xref: /haiku/src/apps/terminal/TermApp.cpp (revision 0ce4c23d22fae64d10e5575687490fbdf8ee52b8)
1 /*
2  * Copyright 2001-2010, Haiku, Inc. All rights reserved.
3  * Copyright (c) 2003-2004 Kian Duffy <myob@users.sourceforge.net>
4  * Copyright (C) 1998,99 Kazuho Okui and Takashi Murai.
5  *
6  * Distributed unter the terms of the MIT license.
7  */
8 
9 
10 #include "TermApp.h"
11 
12 #include <errno.h>
13 #include <signal.h>
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <unistd.h>
17 
18 #include <Alert.h>
19 #include <Catalog.h>
20 #include <Clipboard.h>
21 #include <Catalog.h>
22 #include <InterfaceDefs.h>
23 #include <Locale.h>
24 #include <NodeInfo.h>
25 #include <Path.h>
26 #include <Roster.h>
27 #include <Screen.h>
28 #include <String.h>
29 
30 #include "Arguments.h"
31 #include "Globals.h"
32 #include "PrefHandler.h"
33 #include "TermConst.h"
34 #include "TermWindow.h"
35 
36 
37 static bool sUsageRequested = false;
38 //static bool sGeometryRequested = false;
39 
40 
41 int
42 main()
43 {
44 	TermApp app;
45 	app.Run();
46 
47 	return 0;
48 }
49 
50 #undef B_TRANSLATION_CONTEXT
51 #define B_TRANSLATION_CONTEXT "Terminal TermApp"
52 
53 TermApp::TermApp()
54 	: BApplication(TERM_SIGNATURE),
55 	fStartFullscreen(false),
56 	fTermWindow(NULL),
57 	fArgs(NULL)
58 {
59 	fArgs = new Arguments(0, NULL);
60 }
61 
62 
63 TermApp::~TermApp()
64 {
65 	delete fArgs;
66 }
67 
68 
69 void
70 TermApp::ReadyToRun()
71 {
72 	// Prevent opeing window when option -h is given.
73 	if (sUsageRequested)
74 		return;
75 
76 	// Install a SIGCHLD signal handler, so that we will be notified, when
77 	// a shell exits.
78 	struct sigaction action;
79 #ifdef __HAIKU__
80 	action.sa_handler = (__sighandler_t)_SigChildHandler;
81 #else
82 	action.sa_handler = (__signal_func_ptr)_SigChildHandler;
83 #endif
84 	sigemptyset(&action.sa_mask);
85 #ifdef SA_NODEFER
86 	action.sa_flags = SA_NODEFER;
87 #endif
88 	action.sa_userdata = this;
89 	if (sigaction(SIGCHLD, &action, NULL) < 0) {
90 		fprintf(stderr, "sigaction() failed: %s\n",
91 			strerror(errno));
92 		// continue anyway
93 	}
94 
95 	// init the mouse copy'n'paste clipboard
96 	gMouseClipboard = new BClipboard(MOUSE_CLIPBOARD_NAME, true);
97 
98 	status_t status = _MakeTermWindow();
99 
100 	// failed spawn, print stdout and open alert panel
101 	// TODO: This alert does never show up.
102 	if (status < B_OK) {
103 		BAlert* alert = new BAlert("alert",
104 			B_TRANSLATE("Terminal couldn't start the shell. Sorry."),
105 			B_TRANSLATE("OK"), NULL, NULL, B_WIDTH_FROM_LABEL,
106 			B_INFO_ALERT);
107 		alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
108 		alert->Go(NULL);
109 		PostMessage(B_QUIT_REQUESTED);
110 		return;
111 	}
112 
113 	// using BScreen::Frame isn't enough
114 	if (fStartFullscreen)
115 		BMessenger(fTermWindow).SendMessage(FULLSCREEN);
116 }
117 
118 
119 bool
120 TermApp::QuitRequested()
121 {
122 	// check whether the system is shutting down
123 	BMessage* message = CurrentMessage();
124 	bool shutdown;
125 	if (message != NULL && message->FindBool("_shutdown_", &shutdown) == B_OK
126 		&& shutdown) {
127 		// The system is shutting down. Quit the window synchronously. This
128 		// skips the checks for running processes and the "Are you sure..."
129 		// alert.
130 		if (fTermWindow->Lock())
131 			fTermWindow->Quit();
132 	}
133 
134 	return BApplication::QuitRequested();
135 }
136 
137 
138 void
139 TermApp::Quit()
140 {
141 	BApplication::Quit();
142 }
143 
144 
145 void
146 TermApp::MessageReceived(BMessage* message)
147 {
148 	switch (message->what) {
149 		case MSG_ACTIVATE_TERM:
150 			fTermWindow->Activate();
151 			break;
152 
153 		case MSG_CHECK_CHILDREN:
154 			_HandleChildCleanup();
155 			break;
156 
157 		default:
158 			BApplication::MessageReceived(message);
159 			break;
160 	}
161 }
162 
163 
164 void
165 TermApp::ArgvReceived(int32 argc, char **argv)
166 {
167 	fArgs->Parse(argc, argv);
168 
169 	if (fArgs->UsageRequested()) {
170 		_Usage(argv[0]);
171 		sUsageRequested = true;
172 		PostMessage(B_QUIT_REQUESTED);
173 		return;
174 	}
175 
176 	if (fArgs->Title() != NULL)
177 		fWindowTitle = fArgs->Title();
178 
179 	fStartFullscreen = fArgs->FullScreen();
180 }
181 
182 
183 void
184 TermApp::RefsReceived(BMessage* message)
185 {
186 	// Works Only Launced by Double-Click file, or Drags file to App.
187 	if (!IsLaunching())
188 		return;
189 
190 	entry_ref ref;
191 	if (message->FindRef("refs", 0, &ref) != B_OK)
192 		return;
193 
194 	BFile file;
195 	if (file.SetTo(&ref, B_READ_WRITE) != B_OK)
196 		return;
197 
198 	BNodeInfo info(&file);
199 	char mimetype[B_MIME_TYPE_LENGTH];
200 	info.GetType(mimetype);
201 
202 	// if App opened by Pref file
203 	if (strcmp(mimetype, PREFFILE_MIMETYPE) == 0) {
204 
205 		BEntry ent(&ref);
206 		BPath path(&ent);
207 		PrefHandler::Default()->OpenText(path.Path());
208 		return;
209 	}
210 
211 	// if App opened by Shell Script
212 	if (strcmp(mimetype, "text/x-haiku-shscript") == 0) {
213 		// Not implemented.
214 		//    beep();
215 		return;
216 	}
217 }
218 
219 
220 status_t
221 TermApp::_MakeTermWindow()
222 {
223 	try {
224 		fTermWindow = new TermWindow(fWindowTitle, fArgs);
225 	} catch (int error) {
226 		return (status_t)error;
227 	} catch (...) {
228 		return B_ERROR;
229 	}
230 
231 	fTermWindow->Show();
232 
233 	return B_OK;
234 }
235 
236 
237 //#ifndef B_NETPOSITIVE_APP_SIGNATURE
238 //#define B_NETPOSITIVE_APP_SIGNATURE "application/x-vnd.Be-NPOS"
239 //#endif
240 //
241 //void
242 //TermApp::ShowHTML(BMessage *msg)
243 //{
244 //  const char *url;
245 //  msg->FindString("Url", &url);
246 //  BMessage message;
247 //
248 //  message.what = B_NETPOSITIVE_OPEN_URL;
249 //  message.AddString("be:url", url);
250 
251 //  be_roster->Launch(B_NETPOSITIVE_APP_SIGNATURE, &message);
252 //  while(!(be_roster->IsRunning(B_NETPOSITIVE_APP_SIGNATURE)))
253 //    snooze(10000);
254 //
255 //  // Activate net+
256 //  be_roster->ActivateApp(be_roster->TeamFor(B_NETPOSITIVE_APP_SIGNATURE));
257 //}
258 
259 
260 void
261 TermApp::_HandleChildCleanup()
262 {
263 }
264 
265 
266 /*static*/ void
267 TermApp::_SigChildHandler(int signal, void* data)
268 {
269 	// Spawing a thread that does the actual signal handling is pretty much
270 	// the only safe thing to do in a multi-threaded application. The
271 	// interrupted thread might have been anywhere, e.g. in a critical section,
272 	// holding locks. If we do anything that does require locking at any point
273 	// (e.g. memory allocation, messaging), we risk a dead-lock or data
274 	// structure corruption. Spawing a thread is safe though, since its only
275 	// a system call.
276 	thread_id thread = spawn_thread(_ChildCleanupThread, "child cleanup",
277 		B_NORMAL_PRIORITY, ((TermApp*)data)->fTermWindow);
278 	if (thread >= 0)
279 		resume_thread(thread);
280 }
281 
282 
283 /*static*/ status_t
284 TermApp::_ChildCleanupThread(void* data)
285 {
286 	// Just drop the windowa message and let it do the actual work. This
287 	// saves us additional synchronization measures.
288 	return ((TermWindow*)data)->PostMessage(MSG_CHECK_CHILDREN);
289 }
290 
291 
292 
293 void
294 TermApp::_Usage(char *name)
295 {
296 	fprintf(stderr, B_TRANSLATE("Haiku Terminal\n"
297 		"Copyright 2001-2009 Haiku, Inc.\n"
298 		"Copyright(C) 1999 Kazuho Okui and Takashi Murai.\n"
299 		"\n"
300 		"Usage: %s [OPTION] [SHELL]\n"), name);
301 
302 	fprintf(stderr,
303 		B_TRANSLATE("  -h,     --help               print this help\n"
304 		//"  -p,     --preference         load preference file\n"
305 		"  -t,     --title              set window title\n"
306 		"  -f,     --fullscreen         start fullscreen\n")
307 		//"  -geom,  --geometry           set window geometry\n"
308 		//"                               An example of geometry is \"80x25+100+100\"\n"
309 		);
310 }
311 
312