Wayland support#4
Conversation
Clarified Wayland support status and updated known issues section.
… logic as it is not really needed
There was a problem hiding this comment.
Pull request overview
This PR introduces initial Wayland support for runwhenidle by using the ext_idle_notification_v1 protocol, while keeping the existing X11/XScreenSaver polling as a fallback. It also adds helpers to infer missing graphical-session environment variables and a few small utility modules needed by the new backend.
Changes:
- Add a Wayland idle backend (ext-idle-notify) with a poll-based event loop and PID-exit monitoring.
- Add “best effort” inference for
XDG_RUNTIME_DIR,WAYLAND_DISPLAY, andDISPLAYplus basic file/string utilities. - Update build/linking and README to reflect Wayland support and new sources.
Reviewed changes
Copilot reviewed 15 out of 15 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| wayland.h | Declares Wayland idle monitoring entry points. |
| wayland.c | Implements Wayland connection/registry binding and idle notification object management. |
| environment_guessing.h | Declares helpers to infer session env vars and open X11 display best-effort. |
| environment_guessing.c | Implements env inference + Wayland/X11 socket discovery logic. |
| ext-idle-notify-v1-client-protocol.h | Generated client header for ext-idle-notify-v1 protocol. |
| ext-idle-notify-v1-protocol.c | Generated protocol interface definitions for linking against libwayland-client. |
| main.c | Integrates Wayland event loop first, then falls back to X11 polling; adds SIGCHLD handling and timer/poll logic. |
| process_handling.h | Exposes a helper to open a pidfd for process-exit monitoring. |
| process_handling.c | Implements pidfd open via syscall() with ENOSYS fallback. |
| file_utils.h | Declares filesystem helpers used by environment inference. |
| file_utils.c | Implements socket/dir/file checks and home-dir lookup. |
| string_utils.h | Declares is_string_null_or_empty. |
| string_utils.c | Implements is_string_null_or_empty. |
| README.md | Documents Wayland vs X11 behavior and updates known-issues note. |
| Makefile | Links Wayland client library and builds the new sources. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 17 out of 17 changed files in this pull request and generated 10 comments.
Comments suppressed due to low confidence (1)
main.c:464
- After introducing the
resume_paused_command_on_user_idle()helper, the original inline logic (theif (!quiet)printf block, the call toresume_command_recursively(pid), and thecommand_paused = 0assignment) was not removed. As a result, when the user becomes idle and the command is resumed, the "Lack of user activity detected." message is printed twice andresume_command_recursively(pid)is invoked twice. The duplicated block should be deleted.
resume_paused_command_on_user_idle();
if (!quiet) {
printf("Lack of user activity detected. ");
//intentionally no new line here, resume_command will print the rest of the message.
}
resume_command_recursively(pid);
command_paused = 0;
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
… suffixes starting with a digit (e.g., `wayland-1abc`) were incorrectly accepted and parsed as numeric.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 17 out of 17 changed files in this pull request and generated 5 comments.
Comments suppressed due to low confidence (1)
main.c:466
- After extracting the resume helper, the original message-and-resume block was kept in place. The result is that when the user becomes idle on X11,
resume_paused_command_on_user_idle()first prints "Lack of user activity detected." and callsresume_command_recursively(pid), and then lines 461–466 print the same message and callresume_command_recursively(pid)a second time. The command is resumed twice (sending SIGCONT to an already-running process is harmless but misleading) and the user-facing message is duplicated. The leftoverif (!quiet) { printf(...); }+resume_command_recursively(pid); command_paused = 0;block should be removed sinceresume_paused_command_on_user_idle()already performs all of it.
resume_paused_command_on_user_idle();
if (!quiet) {
printf("Lack of user activity detected. ");
//intentionally no new line here, resume_command will print the rest of the message.
}
resume_command_recursively(pid);
command_paused = 0;
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 17 out of 17 changed files in this pull request and generated 6 comments.
Comments suppressed due to low confidence (1)
main.c:465
- Duplicate resume on idle:
resume_paused_command_on_user_idle()already prints "Lack of user activity detected. ", callsresume_command_recursively(pid), and setscommand_paused = 0. The block below it (lines 460–465) repeats all three steps, so on every idle transition the command is resumed twice and the message is printed twice. Remove the duplicated block.
resume_paused_command_on_user_idle();
if (!quiet) {
printf("Lack of user activity detected. ");
//intentionally no new line here, resume_command will print the rest of the message.
}
resume_command_recursively(pid);
command_paused = 0;
…m_home_dir` where a long home directory path could lead to an incorrect `.Xauthority` path. README
| if (wayland_seat == NULL || wayland_idle_notifier == NULL) { | ||
| wl_registry_destroy(wayland_registry); | ||
| wayland_registry = NULL; | ||
| wl_display_disconnect(wayland_display); | ||
| wayland_display = NULL; | ||
| wayland_seat = NULL; | ||
| wayland_idle_notifier = NULL; | ||
| wayland_idle_notify_available = 0; | ||
| return 0; | ||
| } |
| process_exit_wait_file_descriptor = open_pid_file_descriptor_for_process(pid); | ||
| if (process_exit_wait_file_descriptor == -1) { | ||
| const int saved_errno = errno; | ||
| fprintf_error("Failed to open file descriptor for pid %d: %s\n", pid, strerror(saved_errno)); | ||
|
|
||
| if (external_pid != 0) { | ||
| external_pid_fallback_check_timer_file_descriptor = create_periodic_timer_file_descriptor_every_ms(1000); | ||
| if (external_pid_fallback_check_timer_file_descriptor == -1) { | ||
| const int timer_errno = errno; | ||
| fprintf_error("Failed to create periodic timer file descriptor for external pid fallback: %s\n", | ||
| strerror(timer_errno)); | ||
| goto run_wayland_idle_event_loop_cleanup; | ||
| } | ||
| } | ||
| } |
| } | ||
|
|
||
| close_file_descriptor_if_open(&start_monitor_timer_file_descriptor, "start-monitor timer"); | ||
| close_file_descriptor_if_open(&process_exit_wait_file_descriptor, "process-exit"); | ||
| close_file_descriptor_if_open(&external_pid_fallback_check_timer_file_descriptor, "external-pid fallback timer"); | ||
| if (verbose) { | ||
| fprintf(stderr, "Wayland connection lost or loop finished.\n"); | ||
| } | ||
| return wait_for_pid_to_exit_checking_for_signals(); |
| if (wayland_seat == NULL || wayland_idle_notifier == NULL) { | ||
| wl_registry_destroy(wayland_registry); | ||
| wayland_registry = NULL; | ||
| wl_display_disconnect(wayland_display); | ||
| wayland_display = NULL; | ||
| wayland_seat = NULL; | ||
| wayland_idle_notifier = NULL; | ||
| wayland_idle_notify_available = 0; | ||
| return 0; | ||
| } |
| int start_wayland_idle_notification_object( | ||
| const struct ext_idle_notification_v1_listener *wayland_idle_notification_listener) { | ||
| if (wayland_idle_notification != NULL) { | ||
| return 0; | ||
| } | ||
| if (!wayland_idle_notify_available) { | ||
| return -1; | ||
| } | ||
|
|
||
| uint32_t timeout_ms_for_protocol = (user_idle_timeout_ms > UINT32_MAX) | ||
| ? UINT32_MAX | ||
| : (uint32_t) user_idle_timeout_ms; | ||
|
|
||
| if (wayland_idle_notifier_version >= 2) { | ||
| wayland_idle_notification = ext_idle_notifier_v1_get_input_idle_notification( | ||
| wayland_idle_notifier, timeout_ms_for_protocol, wayland_seat); | ||
| } else { | ||
| wayland_idle_notification = ext_idle_notifier_v1_get_idle_notification( | ||
| wayland_idle_notifier, timeout_ms_for_protocol, wayland_seat); | ||
| } | ||
|
|
||
| if (!wayland_idle_notification) { | ||
| return -1; | ||
| } | ||
|
|
||
| ext_idle_notification_v1_add_listener(wayland_idle_notification, wayland_idle_notification_listener, NULL); | ||
| wl_display_flush(wayland_display); | ||
| return 1; | ||
| } |
| if (!found) { | ||
| found = 1; | ||
| best_numeric_suffix = (numeric_suffix >= 0) ? numeric_suffix : INT_MAX; | ||
| snprintf(best_name, sizeof(best_name), "%s", entry->d_name); | ||
| continue; | ||
| } | ||
|
|
||
| if (numeric_suffix >= 0 && numeric_suffix < best_numeric_suffix) { | ||
| best_numeric_suffix = numeric_suffix; | ||
| snprintf(best_name, sizeof(best_name), "%s", entry->d_name); | ||
| continue; | ||
| } | ||
|
|
||
| if (best_numeric_suffix == INT_MAX && numeric_suffix == -1) { | ||
| snprintf(best_name, sizeof(best_name), "%s", entry->d_name); | ||
| } | ||
| } |
| const char *digits = entry->d_name + 1; | ||
| if (*digits < '0' || *digits > '9') { | ||
| continue; | ||
| } | ||
|
|
||
| int display_number = (int)strtol(digits, NULL, 10); | ||
|
|
||
| char socket_path[PATH_MAX]; | ||
| snprintf(socket_path, sizeof(socket_path), "%s/%s", x11_socket_dir, entry->d_name); | ||
|
|
||
| if (!file_is_socket(socket_path)) { | ||
| continue; | ||
| } |
No description provided.