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 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(); 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 B_KEY_MAP_LOADED: 183 fTermWindow->PostMessage(message); 184 break; 185 186 case MSG_ACTIVATE_TERM: 187 fTermWindow->Activate(); 188 break; 189 190 default: 191 BApplication::MessageReceived(message); 192 break; 193 } 194 } 195 196 197 void 198 TermApp::ArgvReceived(int32 argc, char **argv) 199 { 200 fArgs->Parse(argc, argv); 201 202 if (fArgs->UsageRequested()) { 203 _Usage(argv[0]); 204 sUsageRequested = true; 205 PostMessage(B_QUIT_REQUESTED); 206 return; 207 } 208 209 if (fArgs->Title() != NULL) 210 fWindowTitle = fArgs->Title(); 211 212 if (fArgs->WorkingDir() != NULL) { 213 fWorkingDirectory = fArgs->WorkingDir(); 214 chdir(fWorkingDirectory); 215 } 216 217 fStartFullscreen = fArgs->FullScreen(); 218 } 219 220 221 void 222 TermApp::RefsReceived(BMessage* message) 223 { 224 // Works Only Launced by Double-Click file, or Drags file to App. 225 if (!IsLaunching()) 226 return; 227 228 entry_ref ref; 229 if (message->FindRef("refs", 0, &ref) != B_OK) 230 return; 231 232 BFile file; 233 if (file.SetTo(&ref, B_READ_WRITE) != B_OK) 234 return; 235 236 BNodeInfo info(&file); 237 char mimetype[B_MIME_TYPE_LENGTH]; 238 info.GetType(mimetype); 239 240 // if App opened by Pref file 241 if (strcmp(mimetype, PREFFILE_MIMETYPE) == 0) { 242 243 BEntry ent(&ref); 244 BPath path(&ent); 245 PrefHandler::Default()->OpenText(path.Path()); 246 return; 247 } 248 249 // if App opened by Shell Script 250 if (strcmp(mimetype, "text/x-haiku-shscript") == 0) { 251 // Not implemented. 252 // beep(); 253 return; 254 } 255 } 256 257 258 status_t 259 TermApp::_MakeTermWindow() 260 { 261 try { 262 fTermWindow = new TermWindow(fWindowTitle, fArgs); 263 } catch (int error) { 264 return (status_t)error; 265 } catch (...) { 266 return B_ERROR; 267 } 268 269 fTermWindow->Show(); 270 271 return B_OK; 272 } 273 274 275 //#ifndef B_NETPOSITIVE_APP_SIGNATURE 276 //#define B_NETPOSITIVE_APP_SIGNATURE "application/x-vnd.Be-NPOS" 277 //#endif 278 // 279 //void 280 //TermApp::ShowHTML(BMessage *msg) 281 //{ 282 // const char *url; 283 // msg->FindString("Url", &url); 284 // BMessage message; 285 // 286 // message.what = B_NETPOSITIVE_OPEN_URL; 287 // message.AddString("be:url", url); 288 289 // be_roster->Launch(B_NETPOSITIVE_APP_SIGNATURE, &message); 290 // while(!(be_roster->IsRunning(B_NETPOSITIVE_APP_SIGNATURE))) 291 // snooze(10000); 292 // 293 // // Activate net+ 294 // be_roster->ActivateApp(be_roster->TeamFor(B_NETPOSITIVE_APP_SIGNATURE)); 295 //} 296 297 298 /*static*/ void 299 TermApp::_SigChildHandler(int signal, void* data) 300 { 301 fprintf(stderr, "Terminal: _SigChildHandler() called! That should never " 302 "happen!\n"); 303 } 304 305 306 /*static*/ status_t 307 TermApp::_ChildCleanupThreadEntry(void* data) 308 { 309 return ((TermApp*)data)->_ChildCleanupThread(); 310 } 311 312 313 status_t 314 TermApp::_ChildCleanupThread() 315 { 316 sigset_t waitForSignals; 317 sigemptyset(&waitForSignals); 318 sigaddset(&waitForSignals, SIGCHLD); 319 sigaddset(&waitForSignals, SIGUSR1); 320 321 for (;;) { 322 int signal; 323 int error = sigwait(&waitForSignals, &signal); 324 325 if (fTerminating) 326 break; 327 328 if (error == 0 && signal == SIGCHLD) 329 fTermWindow->PostMessage(MSG_CHECK_CHILDREN); 330 } 331 332 return B_OK; 333 } 334 335 336 void 337 TermApp::_Usage(char *name) 338 { 339 fprintf(stderr, B_TRANSLATE("Haiku Terminal\n" 340 "Copyright 2001-2019 Haiku, Inc.\n" 341 "Copyright(C) 1999 Kazuho Okui and Takashi Murai.\n" 342 "\n" 343 "Usage: %s [OPTION] [SHELL]\n"), name); 344 345 fputs(B_TRANSLATE( 346 " -h, --help print this help\n" 347 //" -p, --preference load preference file\n" 348 " -t, --title set window title\n" 349 " -f, --fullscreen start fullscreen\n" 350 " -w, --working-directory set initial working directory") 351 //" -geom, --geometry set window geometry\n" 352 //" An example of geometry is \"80x25+100+100\"\n" 353 , stderr); 354 } 355 356 357 void 358 TermApp::_InitDefaultPalette() 359 { 360 // 0 - 15 are system ANSI colors 361 const char * keys[kANSIColorCount] = { 362 PREF_ANSI_BLACK_COLOR, 363 PREF_ANSI_RED_COLOR, 364 PREF_ANSI_GREEN_COLOR, 365 PREF_ANSI_YELLOW_COLOR, 366 PREF_ANSI_BLUE_COLOR, 367 PREF_ANSI_MAGENTA_COLOR, 368 PREF_ANSI_CYAN_COLOR, 369 PREF_ANSI_WHITE_COLOR, 370 PREF_ANSI_BLACK_HCOLOR, 371 PREF_ANSI_RED_HCOLOR, 372 PREF_ANSI_GREEN_HCOLOR, 373 PREF_ANSI_YELLOW_HCOLOR, 374 PREF_ANSI_BLUE_HCOLOR, 375 PREF_ANSI_MAGENTA_HCOLOR, 376 PREF_ANSI_CYAN_HCOLOR, 377 PREF_ANSI_WHITE_HCOLOR 378 }; 379 380 rgb_color* color = fDefaultPalette; 381 PrefHandler* handler = PrefHandler::Default(); 382 for (uint i = 0; i < kANSIColorCount; i++) 383 *color++ = handler->getRGB(keys[i]); 384 385 // 16 - 231 are 6x6x6 color "cubes" in xterm color model 386 for (uint red = 0; red < 256; red += (red == 0) ? 95 : 40) 387 for (uint green = 0; green < 256; green += (green == 0) ? 95 : 40) 388 for (uint blue = 0; blue < 256; blue += (blue == 0) ? 95 : 40) 389 (*color++).set_to(red, green, blue); 390 391 // 232 - 255 are grayscale ramp in xterm color model 392 for (uint gray = 8; gray < 240; gray += 10) 393 (*color++).set_to(gray, gray, gray); 394 } 395