main.cpp 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. #include "TerminalWidget.h"
  2. #include <Kernel/KeyCode.h>
  3. #include <LibCore/CArgsParser.h>
  4. #include <LibCore/CUserInfo.h>
  5. #include <LibDraw/PNGLoader.h>
  6. #include <LibGUI/GAboutDialog.h>
  7. #include <LibGUI/GAction.h>
  8. #include <LibGUI/GApplication.h>
  9. #include <LibGUI/GBoxLayout.h>
  10. #include <LibGUI/GFontDatabase.h>
  11. #include <LibGUI/GGroupBox.h>
  12. #include <LibGUI/GMenuBar.h>
  13. #include <LibGUI/GRadioButton.h>
  14. #include <LibGUI/GSlider.h>
  15. #include <LibGUI/GWidget.h>
  16. #include <LibGUI/GWindow.h>
  17. #include <assert.h>
  18. #include <errno.h>
  19. #include <fcntl.h>
  20. #include <pwd.h>
  21. #include <stdio.h>
  22. #include <stdlib.h>
  23. #include <string.h>
  24. #include <sys/ioctl.h>
  25. #include <sys/select.h>
  26. #include <unistd.h>
  27. static void run_command(int ptm_fd, String command)
  28. {
  29. pid_t pid = fork();
  30. if (pid == 0) {
  31. const char* tty_name = ptsname(ptm_fd);
  32. if (!tty_name) {
  33. perror("ptsname");
  34. exit(1);
  35. }
  36. close(ptm_fd);
  37. int pts_fd = open(tty_name, O_RDWR);
  38. if (pts_fd < 0) {
  39. perror("open");
  40. exit(1);
  41. }
  42. // NOTE: It's okay if this fails.
  43. (void)ioctl(0, TIOCNOTTY);
  44. close(0);
  45. close(1);
  46. close(2);
  47. int rc = dup2(pts_fd, 0);
  48. if (rc < 0) {
  49. perror("dup2");
  50. exit(1);
  51. }
  52. rc = dup2(pts_fd, 1);
  53. if (rc < 0) {
  54. perror("dup2");
  55. exit(1);
  56. }
  57. rc = dup2(pts_fd, 2);
  58. if (rc < 0) {
  59. perror("dup2");
  60. exit(1);
  61. }
  62. rc = close(pts_fd);
  63. if (rc < 0) {
  64. perror("close");
  65. exit(1);
  66. }
  67. rc = ioctl(0, TIOCSCTTY);
  68. if (rc < 0) {
  69. perror("ioctl(TIOCSCTTY)");
  70. exit(1);
  71. }
  72. const char* args[4] = { "/bin/Shell", nullptr, nullptr, nullptr };
  73. if (!command.is_empty()) {
  74. args[1] = "-c";
  75. args[2] = command.characters();
  76. }
  77. const char* envs[] = { "TERM=xterm", "PATH=/bin:/usr/bin:/usr/local/bin", nullptr };
  78. rc = execve("/bin/Shell", const_cast<char**>(args), const_cast<char**>(envs));
  79. if (rc < 0) {
  80. perror("execve");
  81. exit(1);
  82. }
  83. ASSERT_NOT_REACHED();
  84. }
  85. }
  86. GWindow* create_settings_window(TerminalWidget& terminal, RefPtr<CConfigFile> config)
  87. {
  88. auto* window = new GWindow;
  89. window->set_title("Terminal Settings");
  90. window->set_rect(50, 50, 200, 140);
  91. auto* settings = new GWidget;
  92. window->set_main_widget(settings);
  93. settings->set_fill_with_background_color(true);
  94. settings->set_layout(make<GBoxLayout>(Orientation::Vertical));
  95. settings->layout()->set_margins({ 4, 4, 4, 4 });
  96. auto radio_container = GGroupBox::construct("Bell Mode", settings);
  97. radio_container->set_layout(make<GBoxLayout>(Orientation::Vertical));
  98. radio_container->layout()->set_margins({ 6, 16, 6, 6 });
  99. radio_container->set_fill_with_background_color(true);
  100. radio_container->set_size_policy(SizePolicy::Fill, SizePolicy::Fixed);
  101. radio_container->set_preferred_size(100, 70);
  102. auto* sysbell_radio = new GRadioButton("Use (Audible) System Bell", radio_container);
  103. auto* visbell_radio = new GRadioButton("Use (Visual) Terminal Bell", radio_container);
  104. sysbell_radio->set_checked(terminal.should_beep());
  105. visbell_radio->set_checked(!terminal.should_beep());
  106. sysbell_radio->on_checked = [&terminal](const bool checked) {
  107. terminal.set_should_beep(checked);
  108. };
  109. auto slider_container = GGroupBox::construct("Background Opacity", settings);
  110. slider_container->set_layout(make<GBoxLayout>(Orientation::Vertical));
  111. slider_container->layout()->set_margins({ 6, 16, 6, 6 });
  112. slider_container->set_fill_with_background_color(true);
  113. slider_container->set_size_policy(SizePolicy::Fill, SizePolicy::Fixed);
  114. slider_container->set_preferred_size(100, 50);
  115. auto* slider = new GSlider(Orientation::Horizontal, slider_container);
  116. slider->set_fill_with_background_color(true);
  117. slider->set_background_color(Color::WarmGray);
  118. slider->on_value_changed = [&terminal, &config](int value) {
  119. terminal.set_opacity(value);
  120. };
  121. slider->set_range(0, 255);
  122. slider->set_value(terminal.opacity());
  123. return window;
  124. }
  125. int main(int argc, char** argv)
  126. {
  127. GApplication app(argc, argv);
  128. CArgsParser args_parser("Terminal");
  129. args_parser.add_arg("e", "execute", "Execute this command inside the terminal.");
  130. CArgsParserResult args = args_parser.parse(argc, argv);
  131. if (chdir(get_current_user_home_path().characters()) < 0)
  132. perror("chdir");
  133. int ptm_fd = open("/dev/ptmx", O_RDWR);
  134. if (ptm_fd < 0) {
  135. perror("open(ptmx)");
  136. return 1;
  137. }
  138. run_command(ptm_fd, args.get("e"));
  139. auto* window = new GWindow;
  140. window->set_title("Terminal");
  141. window->set_background_color(Color::Black);
  142. window->set_double_buffering_enabled(false);
  143. RefPtr<CConfigFile> config = CConfigFile::get_for_app("Terminal");
  144. auto* terminal = new TerminalWidget(ptm_fd, config);
  145. window->set_main_widget(terminal);
  146. window->move_to(300, 300);
  147. terminal->apply_size_increments_to_window(*window);
  148. window->show();
  149. window->set_icon(load_png("/res/icons/16x16/app-terminal.png"));
  150. terminal->set_should_beep(config->read_bool_entry("Window", "AudibleBeep", false));
  151. WeakPtr<GWindow> settings_window;
  152. auto new_opacity = config->read_num_entry("Window", "Opacity", 255);
  153. terminal->set_opacity(new_opacity);
  154. window->set_has_alpha_channel(new_opacity < 255);
  155. auto menubar = make<GMenuBar>();
  156. auto app_menu = make<GMenu>("Terminal");
  157. app_menu->add_action(GAction::create("Settings...", load_png("/res/icons/gear16.png"),
  158. [&settings_window, terminal, &config](const GAction&) {
  159. if (!settings_window)
  160. settings_window = create_settings_window(*terminal, config)->make_weak_ptr();
  161. settings_window->show();
  162. settings_window->move_to_front();
  163. }));
  164. app_menu->add_separator();
  165. app_menu->add_action(GCommonActions::make_quit_action([](auto&) {
  166. dbgprintf("Terminal: Quit menu activated!\n");
  167. GApplication::the().quit(0);
  168. }));
  169. menubar->add_menu(move(app_menu));
  170. auto font_menu = make<GMenu>("Font");
  171. GFontDatabase::the().for_each_fixed_width_font([&](const StringView& font_name) {
  172. font_menu->add_action(GAction::create(font_name, [terminal, &config](const GAction& action) {
  173. terminal->set_font(GFontDatabase::the().get_by_name(action.text()));
  174. auto metadata = GFontDatabase::the().get_metadata_by_name(action.text());
  175. ASSERT(metadata.has_value());
  176. config->write_entry("Text", "Font", metadata.value().path);
  177. config->sync();
  178. terminal->force_repaint();
  179. }));
  180. });
  181. menubar->add_menu(move(font_menu));
  182. auto help_menu = make<GMenu>("Help");
  183. help_menu->add_action(GAction::create("About", [&](const GAction&) {
  184. GAboutDialog::show("Terminal", load_png("/res/icons/32x32/app-terminal.png"), window);
  185. }));
  186. menubar->add_menu(move(help_menu));
  187. app.set_menubar(move(menubar));
  188. config->sync();
  189. return app.exec();
  190. }