main.cpp 7.0 KB

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