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
37static bool sUsageRequested = false;
38//static bool sGeometryRequested = false;
39
40
41int
42main()
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
53TermApp::TermApp()
54	: BApplication(TERM_SIGNATURE),
55	fStartFullscreen(false),
56	fTermWindow(NULL),
57	fArgs(NULL)
58{
59	fArgs = new Arguments(0, NULL);
60}
61
62
63TermApp::~TermApp()
64{
65	delete fArgs;
66}
67
68
69void
70TermApp::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
119bool
120TermApp::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
138void
139TermApp::Quit()
140{
141	BApplication::Quit();
142}
143
144
145void
146TermApp::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
164void
165TermApp::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
183void
184TermApp::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
220status_t
221TermApp::_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
260void
261TermApp::_HandleChildCleanup()
262{
263}
264
265
266/*static*/ void
267TermApp::_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
284TermApp::_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
293void
294TermApp::_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