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