main.cpp 6.5 KB

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