#include #include #include #include #include #include #include #include #include #include #include #include #include #define EPOLL_EVENTS 8 enum app_event { EVENT_UDEV = 0, EVENT_WAYLAND = 1, EVENT_CHILD = 2, }; enum app_state { STATE_POLLING, STATE_RUNNING, STATE_FINISHED, }; /*Open a connection with the current wayland compositor if it exists * A fd is provided which can be polled. When polling the fd returns an error * send a signal to any children and terminate. */ struct poll_phone { enum app_state state; pid_t child; int pipe; }; void app_abort(char *str) { fprintf(stderr, "%s\n", str); exit(-1); } static struct poll_phone app; void run_scrcpy() { assert(app.state != STATE_RUNNING); pid_t pid = fork(); if(pid == 0) { signal(SIGTERM, SIG_DFL); signal(SIGINT, SIG_DFL); /*There's a race condition with adb...*/ sleep(2); execlp("scrcpy", "scrcpy", (char *) 0); exit(-1); } else if(pid == -1) { app_abort("fork() error"); } app.state = STATE_RUNNING; app.child = pid; } void handle_term(int sig) { signal(sig, SIG_IGN); app.state = STATE_FINISHED; } void handle_child(int sig) { char data[1] = {0}; app.state = STATE_POLLING; if(write(app.pipe, data, 1) == -1) { app_abort("write failed"); } } /*Refactor this so we poll the signal handlers*/ int main(int argc, char **argv) { int fileds[2]; char buffer[128]; if(pipe(fileds) == -1) { app_abort("Failled to create pipe."); } app.state = STATE_POLLING; app.pipe = fileds[1]; app.child = -1; signal(SIGTERM, handle_term); signal(SIGINT, handle_term); signal(SIGCHLD, handle_child); struct epoll_event events[EPOLL_EVENTS]; struct epoll_event event; struct wl_display *display = wl_display_connect(NULL); if(!display) { app_abort("Failled to connect to Wayland display."); } struct udev *_udev = udev_new(); if(!_udev) { app_abort("Failed to allocate udev"); } struct udev_monitor *_monitor = udev_monitor_new_from_netlink(_udev, "udev"); if(!_monitor) { app_abort("Failed to allocate udev monitor"); } udev_monitor_enable_receiving(_monitor); int epfd = epoll_create(EPOLL_EVENTS); if(epfd == -1) { app_abort("Failed to create epoll"); } event.events = EPOLLIN; event.data.u32 = EVENT_UDEV; epoll_ctl(epfd, EPOLL_CTL_ADD, udev_monitor_get_fd(_monitor), &event); event.data.u32 = EVENT_WAYLAND; epoll_ctl(epfd, EPOLL_CTL_ADD, wl_display_get_fd(display), &event); event.data.u32 = EVENT_CHILD; epoll_ctl(epfd, EPOLL_CTL_ADD, fileds[0], &event); run_scrcpy(); while(app.state != STATE_FINISHED) { int ready = epoll_wait(epfd, &events[0], EPOLL_EVENTS, -1); if(ready > 0) { for(int i = 0; i < ready; i++) { struct epoll_event *event = &events[i]; switch(event->data.u32) { case EVENT_UDEV: { struct udev_device *device = udev_monitor_receive_device(_monitor); const char *action = udev_device_get_action(device); udev_device_unref(device); if(!strcmp(action, "bind") && app.state == STATE_POLLING) { run_scrcpy(); } break; } case EVENT_WAYLAND: if(wl_display_dispatch(display) == -1) { app.state = STATE_FINISHED; } break; case EVENT_CHILD: { if(read(fileds[0], buffer, 128) == -1) { app_abort("Failed to read"); } if(app.state != STATE_FINISHED) { app.state = STATE_POLLING; } wait(NULL); app.child = -1; } break; } } } } kill(0, SIGTERM); if(app.child != -1) { pid_t r; do { r = wait(NULL); } while (r != app.child && r > 0); } close(fileds[0]); close(fileds[1]); udev_monitor_unref(_monitor); udev_unref(_udev); return 0; }