src/goose.c
changeset 0 5f04caa7212d
equal deleted inserted replaced
-1:000000000000 0:5f04caa7212d
       
     1 #define _POSIX_C_SOURCE 201710L
       
     2 #include <fcntl.h>
       
     3 #include <getopt.h>
       
     4 #include <pango/pangocairo.h>
       
     5 #include <signal.h>
       
     6 #include <stdarg.h>
       
     7 #include <stdlib.h>
       
     8 #include <string.h>
       
     9 #include <sys/socket.h>
       
    10 #include <sys/wait.h>
       
    11 #include <unistd.h>
       
    12 #include "goose.h"
       
    13 
       
    14 
       
    15 /* globals *******************************************************************/
       
    16 
       
    17 struct Server   _server = {0};
       
    18 int             _ipc_socket = -1;
       
    19 struct timespec _start_time = {-1, -1};
       
    20 Verbosity       _verbosity = LOG_LAST;
       
    21 bool            _colored = true;
       
    22 
       
    23 const char* log_colors[] = {
       
    24   [LOG_QUIET] = "",
       
    25   [LOG_ERROR] = "\x1B[1;31m",
       
    26   [LOG_INFO]  = "\x1B[1;32m",
       
    27   [LOG_DEBUG] = "\x1B[1;34m",
       
    28 };
       
    29 const char* log_tags[] = {
       
    30   [LOG_QUIET] = "",
       
    31   [LOG_ERROR] = "[ERROR]",
       
    32   [LOG_INFO]  = "[INFO]",
       
    33   [LOG_DEBUG] = "[DEBUG]",
       
    34 };
       
    35 
       
    36 
       
    37 /* entry *********************************************************************/
       
    38 
       
    39 int
       
    40 main(int argc, char** argv)
       
    41 {
       
    42   int a;
       
    43   if (0 != (a = argue(argc, argv))) {
       
    44     exit(0 > a ? EXIT_FAILURE : EXIT_SUCCESS);
       
    45   }
       
    46   if (!init_all()) {
       
    47     exit(EXIT_FAILURE);
       
    48   }
       
    49   if (!start_server(&_server)) {
       
    50     quit(EXIT_FAILURE);
       
    51   }
       
    52   spawn("imv");
       
    53   run_server(&_server);
       
    54   quit(EXIT_SUCCESS);
       
    55 }
       
    56 
       
    57 
       
    58 int
       
    59 argue(int argc, char** argv)
       
    60 {
       
    61   int opt;
       
    62   struct option longs[] = {
       
    63     { "help", 0, NULL, 'h' },
       
    64     { "quiet", 0, NULL, 'q' },
       
    65     { "verbose", 0, NULL, 'v' },
       
    66     { "version", 0, NULL, 'V' }
       
    67   };
       
    68 
       
    69   while ((opt = getopt_long(argc, argv, "hv", longs, NULL)) != -1) {
       
    70     if ('h' == opt) {
       
    71       help(argv[0], EXIT_SUCCESS);
       
    72       return 1;
       
    73     } else if ('q' == opt) {
       
    74       _verbosity = LOG_QUIET;
       
    75     } else if ('v' == opt) {
       
    76       _verbosity = LOG_INFO;
       
    77       // count 'v's: debug
       
    78     } else if ('V' == opt) {
       
    79       version(argv[0]);
       
    80       return 1;
       
    81     } else {
       
    82       help(argv[0], EXIT_FAILURE);
       
    83       return -1;
       
    84     }
       
    85   }
       
    86   if (optind < argc) {
       
    87     help(argv[0], EXIT_FAILURE);
       
    88     return -1;
       
    89   }
       
    90   return 0;
       
    91 }
       
    92 
       
    93 
       
    94 void
       
    95 help(char* me, int code)
       
    96 {
       
    97   fprintf(EXIT_SUCCESS == code ? stdout : stderr,
       
    98           "Usage: %s -[hv]\n\
       
    99   -h --help     show this help\n\
       
   100   -v --version  show version", me);
       
   101 }
       
   102 
       
   103 
       
   104 void
       
   105 version(char* me)
       
   106 {
       
   107   fprintf(stdout, "%s 0.1", me);
       
   108 }
       
   109 
       
   110 
       
   111 void
       
   112 handle_signal(int signal)
       
   113 {
       
   114   quit(EXIT_SUCCESS);
       
   115 }
       
   116 
       
   117 
       
   118 /* log ***********************************************************************/
       
   119 
       
   120 void
       
   121 init_log()
       
   122 {
       
   123   init_time();
       
   124   // TODO
       
   125 }
       
   126 
       
   127 
       
   128 void
       
   129 init_time(void)
       
   130 {
       
   131   if (0 <= _start_time.tv_sec) {
       
   132     return;
       
   133   }
       
   134   clock_gettime(CLOCK_MONOTONIC, &_start_time);
       
   135 }
       
   136 
       
   137 
       
   138 void
       
   139 honk(Verbosity v, const char* format, ...)
       
   140 {
       
   141   va_list args;
       
   142   va_start(args, format);
       
   143   _honk(v, format, args);
       
   144   va_end(args);
       
   145 }
       
   146 
       
   147 
       
   148 void
       
   149 honk_va(Verbosity v, const char* format, va_list args)
       
   150 {
       
   151   _honk(v, format, args);
       
   152 }
       
   153 
       
   154 
       
   155 void
       
   156 _honk(Verbosity v, const char* format, va_list args)
       
   157 {
       
   158   init_time();
       
   159   if (v > _verbosity) {
       
   160     return;
       
   161   }
       
   162   unsigned c = (LOG_LAST > v) ? v : LOG_LAST - 1;
       
   163   bool colored = _colored && isatty(STDERR_FILENO);
       
   164   if (colored) {
       
   165     fprintf(stderr, "%s", log_colors[c]);
       
   166   }
       
   167   fprintf(stderr, "%s%s ", ME, log_tags[c]);
       
   168   vfprintf(stderr, format, args);
       
   169   if (colored) {
       
   170     fprintf(stderr, "\x1B[0m");
       
   171   }
       
   172   fprintf(stderr, "\n");
       
   173 }
       
   174 
       
   175 
       
   176 /* spawn *********************************************************************/
       
   177 
       
   178 void
       
   179 spawn(char* c)
       
   180 {
       
   181   honk(LOG_DEBUG, "spawning child: %s", c);
       
   182   int p[2];
       
   183   if (0 != pipe(p)) {
       
   184     honk(LOG_ERROR, "cannot create pipe for fork");
       
   185   }
       
   186   pid_t pid = -1;
       
   187   pid_t child = -1;
       
   188   if (0 == (pid = fork())) {
       
   189     setsid();
       
   190     sigset_t set;
       
   191     sigemptyset(&set);
       
   192     sigprocmask(SIG_SETMASK, &set, NULL);
       
   193     close(p[0]);
       
   194     if (0 == (child = fork())) {
       
   195       close(p[1]);
       
   196       honk(LOG_DEBUG, "starting imv");
       
   197       execl("/bin/sh", "/bin/sh", "-c", c, (void*)NULL);
       
   198       exit(0);
       
   199     }
       
   200     close(p[1]);
       
   201     exit(0);
       
   202   } else if (0 > pid) {
       
   203     close(p[0]);
       
   204     close(p[1]);
       
   205     honk(LOG_ERROR, "cannot fork");
       
   206     return;
       
   207   }
       
   208   close(p[1]);
       
   209   close(p[0]);
       
   210   waitpid(pid, NULL, 0);
       
   211   if (0 < child) {
       
   212     honk(LOG_DEBUG, "child created with pid: %d", child);
       
   213   }
       
   214 }
       
   215 
       
   216 
       
   217 /* bump **********************************************************************/
       
   218 
       
   219 
       
   220 /* up ************************************************************************/
       
   221 
       
   222 bool
       
   223 init_all(void) {
       
   224   init_log(_verbosity);
       
   225 
       
   226   if (!getenv("XDG_RUNTIME_DIR")) {
       
   227     honk(LOG_ERROR, "XDG_RUNTIME_DIR must be set");
       
   228     return false;
       
   229   }
       
   230 
       
   231   if (!drop_root()) {
       
   232     return false;
       
   233   }
       
   234 
       
   235   signal(SIGTERM, handle_signal);
       
   236   signal(SIGINT, handle_signal);
       
   237   signal(SIGPIPE, SIG_IGN);
       
   238 
       
   239   prep_init();
       
   240   init_server();
       
   241   init_ipc(&_server);
       
   242   setenv("WAYLAND_DISPLAY", _server.socket, true);
       
   243 
       
   244   return true;
       
   245 }
       
   246 
       
   247 
       
   248 bool
       
   249 drop_root(void)
       
   250 {
       
   251   if (getuid() != geteuid() || getgid() != getegid()) {
       
   252     // set gid first
       
   253     if (0 != setgid(getgid())) {
       
   254       honk(LOG_ERROR, "cannot drop root group");
       
   255       return false;
       
   256     }
       
   257     if (0 != setuid(getuid())) {
       
   258       honk(LOG_ERROR, "cannot drop root user");
       
   259       return false;
       
   260     }
       
   261   }
       
   262   if (-1 != setgid(0) || -1 != setuid(0)) {
       
   263     honk(LOG_ERROR, "cannot drop root");
       
   264     return false;
       
   265   }
       
   266   return true;
       
   267 }
       
   268 
       
   269 
       
   270 bool
       
   271 prep_init(void)
       
   272 {
       
   273   honk(LOG_DEBUG, "preparing server init");
       
   274   _server.display = wl_display_create();
       
   275   _server.event_loop = wl_display_get_event_loop(_server.display);
       
   276   _server.backend = wlr_backend_autocreate(_server.display, NULL);
       
   277   if (!_server.backend) {
       
   278     honk(LOG_ERROR, "cannot create backend");
       
   279     return false;
       
   280   }
       
   281   return true;
       
   282 }
       
   283 
       
   284 
       
   285 bool
       
   286 init_server(void)
       
   287 {
       
   288   honk(LOG_DEBUG, "initialising server");
       
   289   struct wlr_renderer* renderer = wlr_backend_get_renderer(_server.backend);
       
   290   // TODO: what if no renderer?
       
   291   wlr_renderer_init_wl_display(renderer, _server.display);
       
   292   _server.compositor = wlr_compositor_create(_server.display, renderer);
       
   293   _server.compositor_new_surface.notify = handle_compositor_new_surface;
       
   294   wl_signal_add(&_server.compositor->events.new_surface,
       
   295                 &_server.compositor_new_surface);
       
   296   _server.data_device_manager =
       
   297     wlr_data_device_manager_create(_server.display);
       
   298   wlr_gamma_control_manager_v1_create(_server.display);
       
   299   _server.new_output.notify = handle_new_output;
       
   300   wl_signal_add(&_server.backend->events.new_output, &_server.new_output);
       
   301   //_server.output_layout_change.notify = handle_output_layout_change;
       
   302   //wlr_xdg_output_manager_v1_create(_server.display);
       
   303   _server.idle = wlr_idle_create(_server.display);
       
   304   //_server.idle_inhibit_manager = sway_
       
   305   _server.layer_shell = wlr_layer_shell_v1_create(_server.display);
       
   306   wl_signal_add(&_server.layer_shell->events.new_surface,
       
   307                 &_server.layer_shell_surface);
       
   308   _server.layer_shell_surface.notify = handle_layer_shell_surface;
       
   309   _server.xdg_shell = wlr_xdg_shell_create(_server.display);
       
   310   wl_signal_add(&_server.xdg_shell->events.new_surface,
       
   311                 &_server.xdg_shell_surface);
       
   312   _server.xdg_shell_surface.notify = handle_xdg_shell_surface;
       
   313   _server.server_decoration_manager =
       
   314     wlr_server_decoration_manager_create(_server.display);
       
   315   wlr_server_decoration_manager_set_default_mode(
       
   316     _server.server_decoration_manager,
       
   317     WLR_SERVER_DECORATION_MANAGER_MODE_SERVER);
       
   318   wl_signal_add(&_server.server_decoration_manager->events.new_decoration,
       
   319                 &_server.server_decoration);
       
   320   _server.server_decoration.notify = handle_server_decoration;
       
   321   wl_list_init(&_server.server_decorations);
       
   322   _server.xdg_decoration_manager =
       
   323     wlr_xdg_decoration_manager_v1_create(_server.display);
       
   324   wl_signal_add(
       
   325     &_server.xdg_decoration_manager->events.new_toplevel_decoration,
       
   326     &_server.xdg_decoration);
       
   327   _server.xdg_decoration.notify = handle_xdg_decoration;
       
   328   wl_list_init(&_server.xdg_decorations);
       
   329   _server.relative_pointer_manager =
       
   330     wlr_relative_pointer_manager_v1_create(_server.display);
       
   331   _server.pointer_constraints =
       
   332     wlr_pointer_constraints_v1_create(_server.display);
       
   333   _server.pointer_constraint.notify = handle_pointer_constraint;
       
   334   wl_signal_add(&_server.pointer_constraints->events.new_constraint,
       
   335                 &_server.pointer_constraint);
       
   336   _server.presentation =
       
   337     wlr_presentation_create(_server.display, _server.backend);
       
   338   _server.output_manager =
       
   339     wlr_output_manager_v1_create(_server.display);
       
   340   _server.output_manager_apply.notify = handle_output_manager_apply;
       
   341   wl_signal_add(&_server.output_manager->events.apply,
       
   342                 &_server.output_manager_apply);
       
   343   _server.output_manager_test.notify = handle_output_manager_test;
       
   344   wl_signal_add(&_server.output_manager->events.test,
       
   345                 &_server.output_manager_test);
       
   346   _server.output_power_manager =
       
   347     wlr_output_power_manager_v1_create(_server.display);
       
   348   _server.output_power_manager_set_mode.notify =
       
   349     handle_output_power_manager_set_mode;
       
   350   wl_signal_add(&_server.output_power_manager->events.set_mode,
       
   351                 &_server.output_power_manager_set_mode);
       
   352   _server.input_method_manager =
       
   353     wlr_input_method_manager_v2_create(_server.display);
       
   354   _server.text_input_manager =
       
   355     wlr_text_input_manager_v3_create(_server.display);
       
   356   _server.foreign_toplevel_manager =
       
   357     wlr_foreign_toplevel_manager_v1_create(_server.display);
       
   358   wlr_export_dmabuf_manager_v1_create(_server.display);
       
   359   wlr_screencopy_manager_v1_create(_server.display);
       
   360   wlr_data_control_manager_v1_create(_server.display);
       
   361   wlr_primary_selection_v1_device_manager_create(_server.display);
       
   362   wlr_viewporter_create(_server.display);
       
   363 
       
   364   char name[16];
       
   365   for (int i = 1; i <= 32; ++i) {
       
   366     sprintf(name, "wayland-%d", i);
       
   367     if (0 <= wl_display_add_socket(_server.display, name)) {
       
   368       _server.socket = strdup(name);
       
   369       break;
       
   370     }
       
   371   }
       
   372   if (!_server.socket) {
       
   373     honk(LOG_ERROR, "cannot open wayland socket");
       
   374     wlr_backend_destroy(_server.backend);
       
   375     return false;
       
   376   }
       
   377 
       
   378   _server.noop_backend = wlr_noop_backend_create(_server.display);
       
   379   struct wlr_output* output = wlr_noop_add_output(_server.noop_backend);
       
   380   _server.headless_backend =
       
   381     wlr_headless_backend_create_with_renderer(_server.display, renderer);
       
   382   if (!_server.headless_backend) {
       
   383     honk(LOG_INFO, "cannot create headless backend, starting without");
       
   384   } else {
       
   385     wlr_multi_backend_add(_server.backend, _server.headless_backend);
       
   386   }
       
   387 
       
   388   if (!_server.txn_timeout_ms) {
       
   389     _server.txn_timeout_ms = 200;
       
   390   }
       
   391   //_server.dirty_nodes = create_list();
       
   392   //_server.input = input_manager_create(_server);
       
   393   //input_manager_get_default_seat();
       
   394 
       
   395   return true;
       
   396 }
       
   397 
       
   398 
       
   399 void
       
   400 init_ipc(struct Server* server)
       
   401 {
       
   402   _ipc_socket = socket(AF_UNIX, SOCK_STREAM, 0);
       
   403   if (-1 == _ipc_socket) {
       
   404     honk(LOG_ERROR, "cannot create ipc socket");
       
   405     quit(EXIT_FAILURE);
       
   406   }
       
   407   if (-1 == fcntl(_ipc_socket, F_SETFD, FD_CLOEXEC)) {
       
   408     honk(LOG_ERROR, "cannot set CLOEXEC on ipc socket");
       
   409     quit(EXIT_FAILURE);
       
   410   }
       
   411   if (-1 == fcntl(_ipc_socket, F_SETFL, O_NONBLOCK)) {
       
   412     honk(LOG_ERROR, "cannot set NONBLOCK on ipc socket");
       
   413     quit(EXIT_FAILURE);
       
   414   }
       
   415 }
       
   416 
       
   417 
       
   418 bool
       
   419 start_server(struct Server* server)
       
   420 {
       
   421 #ifdef XWAYLAND
       
   422   server->xwayland.xwayland =
       
   423     wlr_xwayland_create(server->display, server->compositor,
       
   424                         false); // XWAYLAND_MODE_LAZY
       
   425   if (!server->xwayland.xwayland) {
       
   426     honk(LOG_ERROR, "cannot start xwayland");
       
   427     unsetenv("DISPLAY");
       
   428   } else {
       
   429     wl_signal_add(&server->xwayland.xwayland->events.new_surface,
       
   430                   &server->xwayland_surface);
       
   431     server->xwayland_surface.notify = handle_xwayland_surface;
       
   432     wl_signal_add(&server->xwayland.xwayland->events.ready,
       
   433                   &server->xwayland_ready);
       
   434     server->xwayland_ready.notify = handle_xwayland_ready;
       
   435     setenv("DISPLAY", server->xwayland.xwayland->display_name, true);
       
   436     /* xcursor configured by default seat */
       
   437   }
       
   438 #endif
       
   439   honk(LOG_INFO, "starting backend on display: %s", server->socket);
       
   440   if (!wlr_backend_start(server->backend)) {
       
   441     honk(LOG_INFO, "cannot start backend");
       
   442     wlr_backend_destroy(server->backend);
       
   443     return false;
       
   444   }
       
   445   return true;
       
   446 }
       
   447 
       
   448 
       
   449 void
       
   450 run_server(struct Server* server)
       
   451 {
       
   452   honk(LOG_INFO, "running %s on display: %s", ME, server->socket);
       
   453   wl_display_run(server->display);
       
   454 }
       
   455 
       
   456 
       
   457 /* down **********************************************************************/
       
   458 
       
   459 void
       
   460 quit(int code) {
       
   461   if (!_server.display) {
       
   462     // ipc client
       
   463     exit(code);
       
   464   }
       
   465   // server
       
   466   wl_display_terminate(_server.display);
       
   467   honk(LOG_INFO, "stopping %s on display: %s", ME, _server.socket);
       
   468   fin_server(&_server);
       
   469   pango_cairo_font_map_set_default(NULL);
       
   470   exit(code);
       
   471 }
       
   472 
       
   473 
       
   474 void
       
   475 fin_server(struct Server* server) {
       
   476 #ifdef XWAYLAND
       
   477   wlr_xwayland_destroy(server->xwayland.xwayland);
       
   478 #endif
       
   479   wl_display_destroy_clients(server->display);
       
   480   wl_display_destroy(server->display);
       
   481 }
       
   482 
       
   483 
       
   484 /* desktop *******************************************************************/
       
   485 
       
   486 void
       
   487 handle_destroy(struct wl_listener* listener, void* data) {
       
   488   // TODO
       
   489 }
       
   490 
       
   491 
       
   492 void
       
   493 handle_compositor_new_surface(struct wl_listener* listener, void* data) {
       
   494   struct wlr_surface* surface = data;
       
   495   // TODO
       
   496 }
       
   497 
       
   498 
       
   499 void
       
   500 handle_new_output(struct wl_listener* listener, void* data) {
       
   501   struct wlr_output* output = data;
       
   502   // TODO
       
   503 }
       
   504 
       
   505 
       
   506 void
       
   507 handle_output_layout_change(struct wl_listener* listener, void* data) {
       
   508   //struct Server* server =
       
   509   //  wl_container_of(listener, server, output_layout_change);
       
   510 }
       
   511 
       
   512 
       
   513 void
       
   514 handle_layer_shell_surface(struct wl_listener* listener, void* data) {
       
   515   struct wlr_layer_surface_v1* surface = data;
       
   516   // TODO
       
   517 }
       
   518 
       
   519 
       
   520 void
       
   521 handle_xdg_shell_surface(struct wl_listener* listener, void* data) {
       
   522   struct wlr_xdg_surface* surface = data;
       
   523   // TODO
       
   524 }
       
   525 
       
   526 
       
   527 void
       
   528 handle_server_decoration(struct wl_listener* listener, void* data) {
       
   529   struct wlr_server_decoration* decoration = data;
       
   530   // TODO
       
   531 }
       
   532 
       
   533 
       
   534 void
       
   535 handle_xdg_decoration(struct wl_listener* listener, void* data) {
       
   536   struct wlr_xdg_toplevel_decoration_v1* decoration = data;
       
   537   // TODO
       
   538 }
       
   539 
       
   540 
       
   541 void
       
   542 handle_pointer_constraint(struct wl_listener* listener, void* data) {
       
   543   struct wlr_pointer_constraint_v1* coinstraint = data;
       
   544   // TODO
       
   545 }
       
   546 
       
   547 
       
   548 void
       
   549 handle_output_manager_apply(struct wl_listener *listener, void *data) {
       
   550   struct wlr_output_configuration_v1* config = data;
       
   551   // TODO
       
   552 }
       
   553 
       
   554 
       
   555 void
       
   556 handle_output_manager_test(struct wl_listener *listener, void *data) {
       
   557   struct wlr_output_configuration_v1* config = data;
       
   558   // TODO
       
   559 }
       
   560 
       
   561 
       
   562 void
       
   563 handle_output_power_manager_set_mode(
       
   564   struct wl_listener *listener, void *data
       
   565 ) {
       
   566   struct wlr_output_power_v1_set_mode_event* event = data;
       
   567   // TODO
       
   568 }
       
   569 
       
   570 
       
   571 /* xwayland ******************************************************************/
       
   572 
       
   573 // TODO: return object
       
   574 void
       
   575 create_unmanaged(struct wlr_xwayland_surface* surface)
       
   576 {
       
   577   return;
       
   578 }
       
   579 
       
   580 
       
   581 // TODO: return object
       
   582 void
       
   583 create_xwayland_view(struct wlr_xwayland_surface* surface)
       
   584 {
       
   585   return;
       
   586 }
       
   587 
       
   588 
       
   589 void
       
   590 handle_xwayland_surface(struct wl_listener* listener, void* data)
       
   591 {
       
   592   struct wlr_xwayland_surface* surface = data;
       
   593   if (surface->override_redirect) {
       
   594     honk(LOG_DEBUG, "new xwayland unmanaged surface");
       
   595     create_unmanaged(surface);
       
   596     return;
       
   597   }
       
   598   create_xwayland_view(surface);
       
   599 }
       
   600 
       
   601 
       
   602 void
       
   603 handle_xwayland_ready(struct wl_listener* listener, void* data)
       
   604 {
       
   605   struct Server* server = wl_container_of(listener, server, xwayland_ready);
       
   606   struct Xwayland* xwayland = &server->xwayland;
       
   607 }
       
   608