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