xref: /haiku/src/apps/terminal/TermApp.cpp (revision 95c9effd68127df2dce202d5e254a7c86560010a)
1 /*
2  * Copyright 2001-2013, 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  * Authors:
9  *		Kian Duffy, myob@users.sourceforge.net
10  *		Siarzhuk Zharski, zharik@gmx.li
11  */
12 
13 
14 #include "TermApp.h"
15 
16 #include <errno.h>
17 #include <signal.h>
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <unistd.h>
21 
22 #include <Alert.h>
23 #include <Catalog.h>
24 #include <Clipboard.h>
25 #include <Catalog.h>
26 #include <InterfaceDefs.h>
27 #include <Locale.h>
28 #include <NodeInfo.h>
29 #include <Path.h>
30 #include <Roster.h>
31 #include <Screen.h>
32 #include <String.h>
33 
34 #include "Arguments.h"
35 #include "Globals.h"
36 #include "PrefHandler.h"
37 #include "TermConst.h"
38 #include "TermWindow.h"
39 
40 
41 static bool sUsageRequested = false;
42 //static bool sGeometryRequested = false;
43 
44 rgb_color TermApp::fDefaultPalette[kTermColorCount];
45 
46 int
47 main()
48 {
49 	TermApp app;
50 	app.Run();
51 
52 	return 0;
53 }
54 
55 #undef B_TRANSLATION_CONTEXT
56 #define B_TRANSLATION_CONTEXT "Terminal TermApp"
57 
58 TermApp::TermApp()
59 	:
60 	BApplication(TERM_SIGNATURE),
61 	fChildCleanupThread(-1),
62 	fTerminating(false),
63 	fStartFullscreen(false),
64 	fTermWindow(NULL),
65 	fArgs(NULL)
66 {
67 	fArgs = new Arguments(0, NULL);
68 
69 	_InitDefaultPalette();
70 }
71 
72 
73 TermApp::~TermApp()
74 {
75 	delete fArgs;
76 }
77 
78 
79 void
80 TermApp::ReadyToRun()
81 {
82 	// Prevent opeing window when option -h is given.
83 	if (sUsageRequested)
84 		return;
85 
86 	// Install a SIGCHLD signal handler, so that we will be notified, when
87 	// a shell exits. The handler itself will never be executed, since we block
88 	// the signal in all threads and handle it with sigwaitinfo() in the child
89 	// cleanup thread.
90 	struct sigaction action;
91 	action.sa_handler = (__sighandler_t)_SigChildHandler;
92 	sigemptyset(&action.sa_mask);
93 	action.sa_flags = 0;
94 	if (sigaction(SIGCHLD, &action, NULL) < 0) {
95 		fprintf(stderr, "sigaction() failed: %s\n", strerror(errno));
96 		// continue anyway
97 	}
98 
99 	// block SIGCHLD and SIGUSR1 -- we send the latter to wake up the child
100 	// cleanup thread when quitting.
101 	sigset_t blockedSignals;
102 	sigemptyset(&blockedSignals);
103 	sigaddset(&blockedSignals, SIGCHLD);
104 	sigaddset(&blockedSignals, SIGUSR1);
105 
106 	int error = pthread_sigmask(SIG_BLOCK, &blockedSignals, NULL);
107 	if (error != 0)
108 		fprintf(stderr, "pthread_sigmask() failed: %s\n", strerror(errno));
109 
110 	// spawn the child cleanup thread
111 	fChildCleanupThread = spawn_thread(_ChildCleanupThreadEntry,
112 		"child cleanup", B_NORMAL_PRIORITY, this);
113 	if (fChildCleanupThread >= 0) {
114 		resume_thread(fChildCleanupThread);
115 	} else {
116 		fprintf(stderr, "Failed to start child cleanup thread: %s\n",
117 			strerror(fChildCleanupThread));
118 	}
119 
120 	// init the mouse copy'n'paste clipboard
121 	gMouseClipboard = new BClipboard(MOUSE_CLIPBOARD_NAME, true);
122 
123 	status_t status = _MakeTermWindow();
124 
125 	// failed spawn, print stdout and open alert panel
126 	// TODO: This alert does never show up.
127 	if (status < B_OK) {
128 		BAlert* alert = new BAlert("alert",
129 			B_TRANSLATE("Terminal couldn't start the shell. Sorry."),
130 			B_TRANSLATE("OK"), NULL, NULL, B_WIDTH_FROM_LABEL,
131 			B_INFO_ALERT);
132 		alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
133 		alert->Go(NULL);
134 		PostMessage(B_QUIT_REQUESTED);
135 		return;
136 	}
137 
138 	// using BScreen::Frame isn't enough
139 	if (fStartFullscreen)
140 		BMessenger(fTermWindow).SendMessage(FULLSCREEN);
141 }
142 
143 
144 bool
145 TermApp::QuitRequested()
146 {
147 	// check whether the system is shutting down
148 	BMessage* message = CurrentMessage();
149 	bool shutdown;
150 	if (message != NULL && message->FindBool("_shutdown_", &shutdown) == B_OK
151 		&& shutdown) {
152 		// The system is shutting down. Quit the window synchronously. This
153 		// skips the checks for running processes and the "Are you sure..."
154 		// alert.
155 		if (fTermWindow->Lock())
156 			fTermWindow->Quit();
157 	}
158 
159 	return BApplication::QuitRequested();
160 }
161 
162 
163 void
164 TermApp::Quit()
165 {
166 	fTerminating = true;
167 
168 	if (fChildCleanupThread >= 0) {
169 		send_signal(fChildCleanupThread, SIGUSR1);
170 		wait_for_thread(fChildCleanupThread, NULL);
171 	}
172 
173 	BApplication::Quit();
174 }
175 
176 
177 void
178 TermApp::MessageReceived(BMessage* message)
179 {
180 	switch (message->what) {
181 		case MSG_ACTIVATE_TERM:
182 			fTermWindow->Activate();
183 			break;
184 
185 		default:
186 			BApplication::MessageReceived(message);
187 			break;
188 	}
189 }
190 
191 
192 void
193 TermApp::ArgvReceived(int32 argc, char **argv)
194 {
195 	fArgs->Parse(argc, argv);
196 
197 	if (fArgs->UsageRequested()) {
198 		_Usage(argv[0]);
199 		sUsageRequested = true;
200 		PostMessage(B_QUIT_REQUESTED);
201 		return;
202 	}
203 
204 	if (fArgs->Title() != NULL)
205 		fWindowTitle = fArgs->Title();
206 
207 	fStartFullscreen = fArgs->FullScreen();
208 }
209 
210 
211 void
212 TermApp::RefsReceived(BMessage* message)
213 {
214 	// Works Only Launced by Double-Click file, or Drags file to App.
215 	if (!IsLaunching())
216 		return;
217 
218 	entry_ref ref;
219 	if (message->FindRef("refs", 0, &ref) != B_OK)
220 		return;
221 
222 	BFile file;
223 	if (file.SetTo(&ref, B_READ_WRITE) != B_OK)
224 		return;
225 
226 	BNodeInfo info(&file);
227 	char mimetype[B_MIME_TYPE_LENGTH];
228 	info.GetType(mimetype);
229 
230 	// if App opened by Pref file
231 	if (strcmp(mimetype, PREFFILE_MIMETYPE) == 0) {
232 
233 		BEntry ent(&ref);
234 		BPath path(&ent);
235 		PrefHandler::Default()->OpenText(path.Path());
236 		return;
237 	}
238 
239 	// if App opened by Shell Script
240 	if (strcmp(mimetype, "text/x-haiku-shscript") == 0) {
241 		// Not implemented.
242 		//    beep();
243 		return;
244 	}
245 }
246 
247 
248 status_t
249 TermApp::_MakeTermWindow()
250 {
251 	try {
252 		fTermWindow = new TermWindow(fWindowTitle, fArgs);
253 	} catch (int error) {
254 		return (status_t)error;
255 	} catch (...) {
256 		return B_ERROR;
257 	}
258 
259 	fTermWindow->Show();
260 
261 	return B_OK;
262 }
263 
264 
265 //#ifndef B_NETPOSITIVE_APP_SIGNATURE
266 //#define B_NETPOSITIVE_APP_SIGNATURE "application/x-vnd.Be-NPOS"
267 //#endif
268 //
269 //void
270 //TermApp::ShowHTML(BMessage *msg)
271 //{
272 //  const char *url;
273 //  msg->FindString("Url", &url);
274 //  BMessage message;
275 //
276 //  message.what = B_NETPOSITIVE_OPEN_URL;
277 //  message.AddString("be:url", url);
278 
279 //  be_roster->Launch(B_NETPOSITIVE_APP_SIGNATURE, &message);
280 //  while(!(be_roster->IsRunning(B_NETPOSITIVE_APP_SIGNATURE)))
281 //    snooze(10000);
282 //
283 //  // Activate net+
284 //  be_roster->ActivateApp(be_roster->TeamFor(B_NETPOSITIVE_APP_SIGNATURE));
285 //}
286 
287 
288 /*static*/ void
289 TermApp::_SigChildHandler(int signal, void* data)
290 {
291 	fprintf(stderr, "Terminal: _SigChildHandler() called! That should never "
292 		"happen!\n");
293 }
294 
295 
296 /*static*/ status_t
297 TermApp::_ChildCleanupThreadEntry(void* data)
298 {
299 	return ((TermApp*)data)->_ChildCleanupThread();
300 }
301 
302 
303 status_t
304 TermApp::_ChildCleanupThread()
305 {
306 	sigset_t waitForSignals;
307 	sigemptyset(&waitForSignals);
308 	sigaddset(&waitForSignals, SIGCHLD);
309 	sigaddset(&waitForSignals, SIGUSR1);
310 
311 	for (;;) {
312 		int signal;
313 		int error = sigwait(&waitForSignals, &signal);
314 
315 		if (fTerminating)
316 			break;
317 
318 		if (error == 0 && signal == SIGCHLD)
319 			fTermWindow->PostMessage(MSG_CHECK_CHILDREN);
320 	}
321 
322 	return B_OK;
323 }
324 
325 
326 void
327 TermApp::_Usage(char *name)
328 {
329 	fprintf(stderr, B_TRANSLATE("Haiku Terminal\n"
330 		"Copyright 2001-2009 Haiku, Inc.\n"
331 		"Copyright(C) 1999 Kazuho Okui and Takashi Murai.\n"
332 		"\n"
333 		"Usage: %s [OPTION] [SHELL]\n"), name);
334 
335 	fprintf(stderr,
336 		B_TRANSLATE("  -h,     --help               print this help\n"
337 		//"  -p,     --preference         load preference file\n"
338 		"  -t,     --title              set window title\n"
339 		"  -f,     --fullscreen         start fullscreen\n")
340 		//"  -geom,  --geometry           set window geometry\n"
341 		//"                               An example of geometry is \"80x25+100+100\"\n"
342 		);
343 }
344 
345 
346 void
347 TermApp::_InitDefaultPalette()
348 {
349 	// 0 - 15 are system ANSI colors
350 	const char * keys[kANSIColorCount] = {
351 		PREF_ANSI_BLACK_COLOR,
352 		PREF_ANSI_RED_COLOR,
353 		PREF_ANSI_GREEN_COLOR,
354 		PREF_ANSI_YELLOW_COLOR,
355 		PREF_ANSI_BLUE_COLOR,
356 		PREF_ANSI_MAGENTA_COLOR,
357 		PREF_ANSI_CYAN_COLOR,
358 		PREF_ANSI_WHITE_COLOR,
359 		PREF_ANSI_BLACK_HCOLOR,
360 		PREF_ANSI_RED_HCOLOR,
361 		PREF_ANSI_GREEN_HCOLOR,
362 		PREF_ANSI_YELLOW_HCOLOR,
363 		PREF_ANSI_BLUE_HCOLOR,
364 		PREF_ANSI_MAGENTA_HCOLOR,
365 		PREF_ANSI_CYAN_HCOLOR,
366 		PREF_ANSI_WHITE_HCOLOR
367 	};
368 
369 	rgb_color* color = fDefaultPalette;
370 	PrefHandler* handler = PrefHandler::Default();
371 	for (uint i = 0; i < kANSIColorCount; i++)
372 		*color++ = handler->getRGB(keys[i]);
373 
374 	// 16 - 231 are 6x6x6 color "cubes" in xterm color model
375 	for (uint red = 0; red < 256; red += (red == 0) ? 95 : 40)
376 		for (uint green = 0; green < 256; green += (green == 0) ? 95 : 40)
377 			for (uint blue = 0; blue < 256; blue += (blue == 0) ? 95 : 40)
378 				(*color++).set_to(red, green, blue);
379 
380 	// 232 - 255 are grayscale ramp in xterm color model
381 	for (uint gray = 8; gray < 240; gray += 10)
382 		(*color++).set_to(gray, gray, gray);
383 }
384