xref: /haiku/src/apps/terminal/TermApp.cpp (revision 14b32de1d5efe99b4c6d4ef8c25df47eb009cf0f)
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 	: BApplication(TERM_SIGNATURE),
60 	fStartFullscreen(false),
61 	fTermWindow(NULL),
62 	fArgs(NULL)
63 {
64 	fArgs = new Arguments(0, NULL);
65 
66 	_InitDefaultPalette();
67 }
68 
69 
70 TermApp::~TermApp()
71 {
72 	delete fArgs;
73 }
74 
75 
76 void
77 TermApp::ReadyToRun()
78 {
79 	// Prevent opeing window when option -h is given.
80 	if (sUsageRequested)
81 		return;
82 
83 	// Install a SIGCHLD signal handler, so that we will be notified, when
84 	// a shell exits.
85 	struct sigaction action;
86 #ifdef __HAIKU__
87 	action.sa_handler = (__sighandler_t)_SigChildHandler;
88 #else
89 	action.sa_handler = (__signal_func_ptr)_SigChildHandler;
90 #endif
91 	sigemptyset(&action.sa_mask);
92 #ifdef SA_NODEFER
93 	action.sa_flags = SA_NODEFER;
94 #endif
95 	action.sa_userdata = this;
96 	if (sigaction(SIGCHLD, &action, NULL) < 0) {
97 		fprintf(stderr, "sigaction() failed: %s\n",
98 			strerror(errno));
99 		// continue anyway
100 	}
101 
102 	// init the mouse copy'n'paste clipboard
103 	gMouseClipboard = new BClipboard(MOUSE_CLIPBOARD_NAME, true);
104 
105 	status_t status = _MakeTermWindow();
106 
107 	// failed spawn, print stdout and open alert panel
108 	// TODO: This alert does never show up.
109 	if (status < B_OK) {
110 		BAlert* alert = new BAlert("alert",
111 			B_TRANSLATE("Terminal couldn't start the shell. Sorry."),
112 			B_TRANSLATE("OK"), NULL, NULL, B_WIDTH_FROM_LABEL,
113 			B_INFO_ALERT);
114 		alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
115 		alert->Go(NULL);
116 		PostMessage(B_QUIT_REQUESTED);
117 		return;
118 	}
119 
120 	// using BScreen::Frame isn't enough
121 	if (fStartFullscreen)
122 		BMessenger(fTermWindow).SendMessage(FULLSCREEN);
123 }
124 
125 
126 bool
127 TermApp::QuitRequested()
128 {
129 	// check whether the system is shutting down
130 	BMessage* message = CurrentMessage();
131 	bool shutdown;
132 	if (message != NULL && message->FindBool("_shutdown_", &shutdown) == B_OK
133 		&& shutdown) {
134 		// The system is shutting down. Quit the window synchronously. This
135 		// skips the checks for running processes and the "Are you sure..."
136 		// alert.
137 		if (fTermWindow->Lock())
138 			fTermWindow->Quit();
139 	}
140 
141 	return BApplication::QuitRequested();
142 }
143 
144 
145 void
146 TermApp::Quit()
147 {
148 	BApplication::Quit();
149 }
150 
151 
152 void
153 TermApp::MessageReceived(BMessage* message)
154 {
155 	switch (message->what) {
156 		case MSG_ACTIVATE_TERM:
157 			fTermWindow->Activate();
158 			break;
159 
160 		case MSG_CHECK_CHILDREN:
161 			_HandleChildCleanup();
162 			break;
163 
164 		default:
165 			BApplication::MessageReceived(message);
166 			break;
167 	}
168 }
169 
170 
171 void
172 TermApp::ArgvReceived(int32 argc, char **argv)
173 {
174 	fArgs->Parse(argc, argv);
175 
176 	if (fArgs->UsageRequested()) {
177 		_Usage(argv[0]);
178 		sUsageRequested = true;
179 		PostMessage(B_QUIT_REQUESTED);
180 		return;
181 	}
182 
183 	if (fArgs->Title() != NULL)
184 		fWindowTitle = fArgs->Title();
185 
186 	fStartFullscreen = fArgs->FullScreen();
187 }
188 
189 
190 void
191 TermApp::RefsReceived(BMessage* message)
192 {
193 	// Works Only Launced by Double-Click file, or Drags file to App.
194 	if (!IsLaunching())
195 		return;
196 
197 	entry_ref ref;
198 	if (message->FindRef("refs", 0, &ref) != B_OK)
199 		return;
200 
201 	BFile file;
202 	if (file.SetTo(&ref, B_READ_WRITE) != B_OK)
203 		return;
204 
205 	BNodeInfo info(&file);
206 	char mimetype[B_MIME_TYPE_LENGTH];
207 	info.GetType(mimetype);
208 
209 	// if App opened by Pref file
210 	if (strcmp(mimetype, PREFFILE_MIMETYPE) == 0) {
211 
212 		BEntry ent(&ref);
213 		BPath path(&ent);
214 		PrefHandler::Default()->OpenText(path.Path());
215 		return;
216 	}
217 
218 	// if App opened by Shell Script
219 	if (strcmp(mimetype, "text/x-haiku-shscript") == 0) {
220 		// Not implemented.
221 		//    beep();
222 		return;
223 	}
224 }
225 
226 
227 status_t
228 TermApp::_MakeTermWindow()
229 {
230 	try {
231 		fTermWindow = new TermWindow(fWindowTitle, fArgs);
232 	} catch (int error) {
233 		return (status_t)error;
234 	} catch (...) {
235 		return B_ERROR;
236 	}
237 
238 	fTermWindow->Show();
239 
240 	return B_OK;
241 }
242 
243 
244 //#ifndef B_NETPOSITIVE_APP_SIGNATURE
245 //#define B_NETPOSITIVE_APP_SIGNATURE "application/x-vnd.Be-NPOS"
246 //#endif
247 //
248 //void
249 //TermApp::ShowHTML(BMessage *msg)
250 //{
251 //  const char *url;
252 //  msg->FindString("Url", &url);
253 //  BMessage message;
254 //
255 //  message.what = B_NETPOSITIVE_OPEN_URL;
256 //  message.AddString("be:url", url);
257 
258 //  be_roster->Launch(B_NETPOSITIVE_APP_SIGNATURE, &message);
259 //  while(!(be_roster->IsRunning(B_NETPOSITIVE_APP_SIGNATURE)))
260 //    snooze(10000);
261 //
262 //  // Activate net+
263 //  be_roster->ActivateApp(be_roster->TeamFor(B_NETPOSITIVE_APP_SIGNATURE));
264 //}
265 
266 
267 void
268 TermApp::_HandleChildCleanup()
269 {
270 }
271 
272 
273 /*static*/ void
274 TermApp::_SigChildHandler(int signal, void* data)
275 {
276 	// Spawing a thread that does the actual signal handling is pretty much
277 	// the only safe thing to do in a multi-threaded application. The
278 	// interrupted thread might have been anywhere, e.g. in a critical section,
279 	// holding locks. If we do anything that does require locking at any point
280 	// (e.g. memory allocation, messaging), we risk a dead-lock or data
281 	// structure corruption. Spawing a thread is safe though, since its only
282 	// a system call.
283 	thread_id thread = spawn_thread(_ChildCleanupThread, "child cleanup",
284 		B_NORMAL_PRIORITY, ((TermApp*)data)->fTermWindow);
285 	if (thread >= 0)
286 		resume_thread(thread);
287 }
288 
289 
290 /*static*/ status_t
291 TermApp::_ChildCleanupThread(void* data)
292 {
293 	// Just drop the windowa message and let it do the actual work. This
294 	// saves us additional synchronization measures.
295 	return ((TermWindow*)data)->PostMessage(MSG_CHECK_CHILDREN);
296 }
297 
298 
299 void
300 TermApp::_Usage(char *name)
301 {
302 	fprintf(stderr, B_TRANSLATE("Haiku Terminal\n"
303 		"Copyright 2001-2009 Haiku, Inc.\n"
304 		"Copyright(C) 1999 Kazuho Okui and Takashi Murai.\n"
305 		"\n"
306 		"Usage: %s [OPTION] [SHELL]\n"), name);
307 
308 	fprintf(stderr,
309 		B_TRANSLATE("  -h,     --help               print this help\n"
310 		//"  -p,     --preference         load preference file\n"
311 		"  -t,     --title              set window title\n"
312 		"  -f,     --fullscreen         start fullscreen\n")
313 		//"  -geom,  --geometry           set window geometry\n"
314 		//"                               An example of geometry is \"80x25+100+100\"\n"
315 		);
316 }
317 
318 
319 void
320 TermApp::_InitDefaultPalette()
321 {
322 	// 0 - 15 are system ANSI colors
323 	const char * keys[kANSIColorCount] = {
324 		PREF_ANSI_BLACK_COLOR,
325 		PREF_ANSI_RED_COLOR,
326 		PREF_ANSI_GREEN_COLOR,
327 		PREF_ANSI_YELLOW_COLOR,
328 		PREF_ANSI_BLUE_COLOR,
329 		PREF_ANSI_MAGENTA_COLOR,
330 		PREF_ANSI_CYAN_COLOR,
331 		PREF_ANSI_WHITE_COLOR,
332 		PREF_ANSI_BLACK_HCOLOR,
333 		PREF_ANSI_RED_HCOLOR,
334 		PREF_ANSI_GREEN_HCOLOR,
335 		PREF_ANSI_YELLOW_HCOLOR,
336 		PREF_ANSI_BLUE_HCOLOR,
337 		PREF_ANSI_MAGENTA_HCOLOR,
338 		PREF_ANSI_CYAN_HCOLOR,
339 		PREF_ANSI_WHITE_HCOLOR
340 	};
341 
342 	rgb_color* color = fDefaultPalette;
343 	PrefHandler* handler = PrefHandler::Default();
344 	for (uint i = 0; i < kANSIColorCount; i++)
345 		*color++ = handler->getRGB(keys[i]);
346 
347 	// 16 - 231 are 6x6x6 color "cubes" in xterm color model
348 	for (uint red = 0; red < 256; red += (red == 0) ? 95 : 40)
349 		for (uint green = 0; green < 256; green += (green == 0) ? 95 : 40)
350 			for (uint blue = 0; blue < 256; blue += (blue == 0) ? 95 : 40)
351 				(*color++).set_to(red, green, blue);
352 
353 	// 232 - 255 are grayscale ramp in xterm color model
354 	for (uint gray = 8; gray < 240; gray += 10)
355 		(*color++).set_to(gray, gray, gray);
356 }
357