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 * Siarzhuk Zharski, <zharik@gmx.li> 12 */ 13 14 15 #include "TermApp.h" 16 17 #include <errno.h> 18 #include <signal.h> 19 #include <stdio.h> 20 #include <stdlib.h> 21 #include <unistd.h> 22 23 #include <Alert.h> 24 #include <Catalog.h> 25 #include <Clipboard.h> 26 #include <Catalog.h> 27 #include <InterfaceDefs.h> 28 #include <Locale.h> 29 #include <NodeInfo.h> 30 #include <Path.h> 31 #include <Roster.h> 32 #include <Screen.h> 33 #include <String.h> 34 35 #include "Arguments.h" 36 #include "Globals.h" 37 #include "PrefHandler.h" 38 #include "TermConst.h" 39 #include "TermWindow.h" 40 41 42 static bool sUsageRequested = false; 43 //static bool sGeometryRequested = false; 44 45 rgb_color TermApp::fDefaultPalette[kTermColorCount]; 46 47 int 48 main() 49 { 50 TermApp app; 51 app.Run(); 52 53 return 0; 54 } 55 56 #undef B_TRANSLATION_CONTEXT 57 #define B_TRANSLATION_CONTEXT "Terminal TermApp" 58 59 TermApp::TermApp() 60 : 61 BApplication(TERM_SIGNATURE), 62 fChildCleanupThread(-1), 63 fTerminating(false), 64 fStartFullscreen(false), 65 fTermWindow(NULL), 66 fArgs(NULL) 67 { 68 fArgs = new Arguments(0, NULL); 69 70 _InitDefaultPalette(); 71 } 72 73 74 TermApp::~TermApp() 75 { 76 delete fArgs; 77 } 78 79 80 void 81 TermApp::ReadyToRun() 82 { 83 // Prevent opeing window when option -h is given. 84 if (sUsageRequested) 85 return; 86 87 // Install a SIGCHLD signal handler, so that we will be notified, when 88 // a shell exits. The handler itself will never be executed, since we block 89 // the signal in all threads and handle it with sigwaitinfo() in the child 90 // cleanup thread. 91 struct sigaction action; 92 action.sa_handler = (__sighandler_t)_SigChildHandler; 93 sigemptyset(&action.sa_mask); 94 action.sa_flags = 0; 95 if (sigaction(SIGCHLD, &action, NULL) < 0) { 96 fprintf(stderr, "sigaction() failed: %s\n", strerror(errno)); 97 // continue anyway 98 } 99 100 // block SIGCHLD and SIGUSR1 -- we send the latter to wake up the child 101 // cleanup thread when quitting. 102 sigset_t blockedSignals; 103 sigemptyset(&blockedSignals); 104 sigaddset(&blockedSignals, SIGCHLD); 105 sigaddset(&blockedSignals, SIGUSR1); 106 107 int error = pthread_sigmask(SIG_BLOCK, &blockedSignals, NULL); 108 if (error != 0) 109 fprintf(stderr, "pthread_sigmask() failed: %s\n", strerror(errno)); 110 111 // spawn the child cleanup thread 112 fChildCleanupThread = spawn_thread(_ChildCleanupThreadEntry, 113 "child cleanup", B_NORMAL_PRIORITY, this); 114 if (fChildCleanupThread >= 0) { 115 resume_thread(fChildCleanupThread); 116 } else { 117 fprintf(stderr, "Failed to start child cleanup thread: %s\n", 118 strerror(fChildCleanupThread)); 119 } 120 121 // init the mouse copy'n'paste clipboard 122 gMouseClipboard = new BClipboard(MOUSE_CLIPBOARD_NAME, true); 123 124 status_t status = _MakeTermWindow(); 125 126 // failed spawn, print stdout and open alert panel 127 // TODO: This alert does never show up. 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(NULL); 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 MSG_ACTIVATE_TERM: 183 fTermWindow->Activate(); 184 break; 185 186 default: 187 BApplication::MessageReceived(message); 188 break; 189 } 190 } 191 192 193 void 194 TermApp::ArgvReceived(int32 argc, char **argv) 195 { 196 fArgs->Parse(argc, argv); 197 198 if (fArgs->UsageRequested()) { 199 _Usage(argv[0]); 200 sUsageRequested = true; 201 PostMessage(B_QUIT_REQUESTED); 202 return; 203 } 204 205 if (fArgs->Title() != NULL) 206 fWindowTitle = fArgs->Title(); 207 208 if (fArgs->WorkingDir() != NULL) { 209 fWorkingDirectory = fArgs->WorkingDir(); 210 chdir(fWorkingDirectory); 211 } 212 213 fStartFullscreen = fArgs->FullScreen(); 214 } 215 216 217 void 218 TermApp::RefsReceived(BMessage* message) 219 { 220 // Works Only Launced by Double-Click file, or Drags file to App. 221 if (!IsLaunching()) 222 return; 223 224 entry_ref ref; 225 if (message->FindRef("refs", 0, &ref) != B_OK) 226 return; 227 228 BFile file; 229 if (file.SetTo(&ref, B_READ_WRITE) != B_OK) 230 return; 231 232 BNodeInfo info(&file); 233 char mimetype[B_MIME_TYPE_LENGTH]; 234 info.GetType(mimetype); 235 236 // if App opened by Pref file 237 if (strcmp(mimetype, PREFFILE_MIMETYPE) == 0) { 238 239 BEntry ent(&ref); 240 BPath path(&ent); 241 PrefHandler::Default()->OpenText(path.Path()); 242 return; 243 } 244 245 // if App opened by Shell Script 246 if (strcmp(mimetype, "text/x-haiku-shscript") == 0) { 247 // Not implemented. 248 // beep(); 249 return; 250 } 251 } 252 253 254 status_t 255 TermApp::_MakeTermWindow() 256 { 257 try { 258 fTermWindow = new TermWindow(fWindowTitle, fArgs); 259 } catch (int error) { 260 return (status_t)error; 261 } catch (...) { 262 return B_ERROR; 263 } 264 265 fTermWindow->Show(); 266 267 return B_OK; 268 } 269 270 271 //#ifndef B_NETPOSITIVE_APP_SIGNATURE 272 //#define B_NETPOSITIVE_APP_SIGNATURE "application/x-vnd.Be-NPOS" 273 //#endif 274 // 275 //void 276 //TermApp::ShowHTML(BMessage *msg) 277 //{ 278 // const char *url; 279 // msg->FindString("Url", &url); 280 // BMessage message; 281 // 282 // message.what = B_NETPOSITIVE_OPEN_URL; 283 // message.AddString("be:url", url); 284 285 // be_roster->Launch(B_NETPOSITIVE_APP_SIGNATURE, &message); 286 // while(!(be_roster->IsRunning(B_NETPOSITIVE_APP_SIGNATURE))) 287 // snooze(10000); 288 // 289 // // Activate net+ 290 // be_roster->ActivateApp(be_roster->TeamFor(B_NETPOSITIVE_APP_SIGNATURE)); 291 //} 292 293 294 /*static*/ void 295 TermApp::_SigChildHandler(int signal, void* data) 296 { 297 fprintf(stderr, "Terminal: _SigChildHandler() called! That should never " 298 "happen!\n"); 299 } 300 301 302 /*static*/ status_t 303 TermApp::_ChildCleanupThreadEntry(void* data) 304 { 305 return ((TermApp*)data)->_ChildCleanupThread(); 306 } 307 308 309 status_t 310 TermApp::_ChildCleanupThread() 311 { 312 sigset_t waitForSignals; 313 sigemptyset(&waitForSignals); 314 sigaddset(&waitForSignals, SIGCHLD); 315 sigaddset(&waitForSignals, SIGUSR1); 316 317 for (;;) { 318 int signal; 319 int error = sigwait(&waitForSignals, &signal); 320 321 if (fTerminating) 322 break; 323 324 if (error == 0 && signal == SIGCHLD) 325 fTermWindow->PostMessage(MSG_CHECK_CHILDREN); 326 } 327 328 return B_OK; 329 } 330 331 332 void 333 TermApp::_Usage(char *name) 334 { 335 fprintf(stderr, B_TRANSLATE("Haiku Terminal\n" 336 "Copyright 2001-2019 Haiku, Inc.\n" 337 "Copyright(C) 1999 Kazuho Okui and Takashi Murai.\n" 338 "\n" 339 "Usage: %s [OPTION] [SHELL]\n"), name); 340 341 fprintf(stderr, B_TRANSLATE( 342 " -h, --help print this help\n" 343 //" -p, --preference load preference file\n" 344 " -t, --title set window title\n" 345 " -f, --fullscreen start fullscreen\n" 346 " -w, --working-directory set initial working directory\n") 347 //" -geom, --geometry set window geometry\n" 348 //" An example of geometry is \"80x25+100+100\"\n" 349 ); 350 } 351 352 353 void 354 TermApp::_InitDefaultPalette() 355 { 356 // 0 - 15 are system ANSI colors 357 const char * keys[kANSIColorCount] = { 358 PREF_ANSI_BLACK_COLOR, 359 PREF_ANSI_RED_COLOR, 360 PREF_ANSI_GREEN_COLOR, 361 PREF_ANSI_YELLOW_COLOR, 362 PREF_ANSI_BLUE_COLOR, 363 PREF_ANSI_MAGENTA_COLOR, 364 PREF_ANSI_CYAN_COLOR, 365 PREF_ANSI_WHITE_COLOR, 366 PREF_ANSI_BLACK_HCOLOR, 367 PREF_ANSI_RED_HCOLOR, 368 PREF_ANSI_GREEN_HCOLOR, 369 PREF_ANSI_YELLOW_HCOLOR, 370 PREF_ANSI_BLUE_HCOLOR, 371 PREF_ANSI_MAGENTA_HCOLOR, 372 PREF_ANSI_CYAN_HCOLOR, 373 PREF_ANSI_WHITE_HCOLOR 374 }; 375 376 rgb_color* color = fDefaultPalette; 377 PrefHandler* handler = PrefHandler::Default(); 378 for (uint i = 0; i < kANSIColorCount; i++) 379 *color++ = handler->getRGB(keys[i]); 380 381 // 16 - 231 are 6x6x6 color "cubes" in xterm color model 382 for (uint red = 0; red < 256; red += (red == 0) ? 95 : 40) 383 for (uint green = 0; green < 256; green += (green == 0) ? 95 : 40) 384 for (uint blue = 0; blue < 256; blue += (blue == 0) ? 95 : 40) 385 (*color++).set_to(red, green, blue); 386 387 // 232 - 255 are grayscale ramp in xterm color model 388 for (uint gray = 8; gray < 240; gray += 10) 389 (*color++).set_to(gray, gray, gray); 390 } 391