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