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