diff --git a/idle-inhibit-unstable-v1.xml b/idle-inhibit-unstable-v1.xml
new file mode 100644
index 0000000..9c06cdc
--- /dev/null
+++ b/idle-inhibit-unstable-v1.xml
@@ -0,0 +1,83 @@
+
+
+
+
+ Copyright © 2015 Samsung Electronics Co., Ltd
+
+ Permission is hereby granted, free of charge, to any person obtaining a
+ copy of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom the
+ Software is furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice (including the next
+ paragraph) shall be included in all copies or substantial portions of the
+ Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ DEALINGS IN THE SOFTWARE.
+
+
+
+
+ This interface permits inhibiting the idle behavior such as screen
+ blanking, locking, and screensaving. The client binds the idle manager
+ globally, then creates idle-inhibitor objects for each surface.
+
+ Warning! The protocol described in this file is experimental and
+ backward incompatible changes may be made. Backward compatible changes
+ may be added together with the corresponding interface version bump.
+ Backward incompatible changes are done by bumping the version number in
+ the protocol and interface names and resetting the interface version.
+ Once the protocol is to be declared stable, the 'z' prefix and the
+ version number in the protocol and interface names are removed and the
+ interface version number is reset.
+
+
+
+
+ Destroy the inhibit manager.
+
+
+
+
+
+ Create a new inhibitor object associated with the given surface.
+
+
+
+
+
+
+
+
+
+ An idle inhibitor prevents the output that the associated surface is
+ visible on from being set to a state where it is not visually usable due
+ to lack of user interaction (e.g. blanked, dimmed, locked, set to power
+ save, etc.) Any screensaver processes are also blocked from displaying.
+
+ If the surface is destroyed, unmapped, becomes occluded, loses
+ visibility, or otherwise becomes not visually relevant for the user, the
+ idle inhibitor will not be honored by the compositor; if the surface
+ subsequently regains visibility the inhibitor takes effect once again.
+ Likewise, the inhibitor isn't honored if the system was already idled at
+ the time the inhibitor was established, although if the system later
+ de-idles and re-idles the inhibitor will take effect.
+
+
+
+
+ Remove the inhibitor effect from the associated wl_surface.
+
+
+
+
+
diff --git a/idle_inhibit.c b/idle_inhibit.c
new file mode 100644
index 0000000..e37ee17
--- /dev/null
+++ b/idle_inhibit.c
@@ -0,0 +1,434 @@
+/* Wayland compositor running on top of an X server.
+
+Copyright (C) 2022 to various contributors.
+
+This file is part of 12to11.
+
+12to11 is free software: you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation, either version 3 of the License, or (at your
+option) any later version.
+
+12to11 is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with 12to11. If not, see . */
+
+#include
+#include
+
+#include "compositor.h"
+#include "idle-inhibit-unstable-v1.h"
+
+typedef struct _IdleInhibitDataRecord IdleInhibitDataRecord;
+typedef struct _IdleInhibitor IdleInhibitor;
+typedef enum _IdleInhibition IdleInhibition;
+
+/* Idle inhibition is tricky because there is no threshold that tells
+ the protocol translator whether or not to apply idle inhibition for
+ surfaces that are already focused. So, contrary to the protocol
+ specification, we inhibit idle-ness as long as a surface with an
+ idle inhibitor is focused, even if the user was already idle at the
+ time the idle inhibitor was created. */
+
+struct _IdleInhibitor
+{
+ /* The next and last idle inhibitors on this surface. */
+ IdleInhibitor *next, *last;
+
+ /* The next and last idle inhibitors globally. */
+ IdleInhibitor *global_next, *global_last;
+
+ /* The corresponding Surface. */
+ Surface *surface;
+
+ /* The corresponding wl_resource. */
+ struct wl_resource *resource;
+};
+
+struct _IdleInhibitDataRecord
+{
+ /* List of idle inhibitors on this surface. */
+ IdleInhibitor inhibitors;
+};
+
+enum _IdleInhibition
+ {
+ IdleAllowed,
+ IdleInhibited,
+ };
+
+/* The idle inhibit manager global. */
+static struct wl_global *idle_inhibit_manager_global;
+
+/* List of all idle inhibitors. */
+static IdleInhibitor all_inhibitors;
+
+/* The current idle inhibition. */
+static IdleInhibition current_inhibition;
+
+/* Commands run while idle. The first command is run once upon idle
+ being inhibited; the second is run every N seconds while idle is
+ inhibited, and the third command is run every time idle is
+ deinhibited. */
+static char **inhibit_command, **timer_command, **deinhibit_command;
+
+/* How many seconds the protocol translator waits before running the
+ timer command. */
+static int timer_seconds;
+
+/* Timer used to run the timer command. */
+static Timer *command_timer;
+
+/* Process queue used to run those commands. */
+static ProcessQueue *process_queue;
+
+static void
+HandleCommandTimer (Timer *timer, void *data, struct timespec time)
+{
+ /* The timer shouldn't have been started if the command is NULL. */
+ RunProcess (process_queue, timer_command);
+}
+
+static void
+ChangeInhibitionTo (IdleInhibition inhibition)
+{
+ if (current_inhibition == inhibition)
+ /* Nothing changed. */
+ return;
+
+ current_inhibition = inhibition;
+
+ if (current_inhibition == IdleInhibited)
+ {
+ /* Run the idle inhibit command, if it exists. */
+
+ if (inhibit_command)
+ RunProcess (process_queue, inhibit_command);
+
+ /* Schedule a timer to run the timer command. */
+
+ if (timer_command)
+ command_timer = AddTimer (HandleCommandTimer, NULL,
+ MakeTimespec (timer_seconds, 0));
+ }
+ else
+ {
+ /* Cancel the command timer. */
+ if (command_timer)
+ RemoveTimer (command_timer);
+ command_timer = NULL;
+
+ /* Run the deinhibit command. */
+
+ if (deinhibit_command)
+ RunProcess (process_queue, deinhibit_command);
+ }
+}
+
+static void
+DetectSurfaceIdleInhibit (void)
+{
+ IdleInhibitor *inhibitor;
+
+ inhibitor = all_inhibitors.global_next;
+ while (inhibitor != &all_inhibitors)
+ {
+ if (inhibitor->surface->num_focused_seats)
+ {
+ ChangeInhibitionTo (IdleInhibited);
+ return;
+ }
+
+ inhibitor = inhibitor->global_next;
+ }
+
+ /* There are no live idle inhibitors for focused seats. */
+ ChangeInhibitionTo (IdleAllowed);
+
+ return;
+}
+
+static void
+NoticeSurfaceFocused (Surface *surface)
+{
+ IdleInhibitDataRecord *record;
+
+ record = XLSurfaceFindClientData (surface, IdleInhibitData);
+
+ if (!record)
+ return;
+
+ if (record->inhibitors.next == &record->inhibitors)
+ return;
+
+ /* There is an idle inhibitor for this idle surface. */
+ ChangeInhibitionTo (IdleInhibited);
+}
+
+
+
+static void
+Destroy (struct wl_client *client, struct wl_resource *resource)
+{
+ wl_resource_destroy (resource);
+}
+
+static const struct zwp_idle_inhibitor_v1_interface idle_inhibitor_impl =
+ {
+ .destroy = Destroy,
+ };
+
+static void
+HandleResourceDestroy (struct wl_resource *resource)
+{
+ IdleInhibitor *inhibitor;
+
+ inhibitor = wl_resource_get_user_data (resource);
+
+ if (inhibitor->surface)
+ {
+ /* Unlink the inhibitor. */
+ inhibitor->next->last = inhibitor->last;
+ inhibitor->last->next = inhibitor->next;
+ inhibitor->global_next->global_last = inhibitor->global_last;
+ inhibitor->global_last->global_next = inhibitor->global_next;
+ }
+
+ /* Free the inhibitor; then, check if any other idle inhibitors are
+ still active. */
+ XLFree (inhibitor);
+ DetectSurfaceIdleInhibit ();
+}
+
+
+
+static void
+FreeIdleInhibitData (void *data)
+{
+ IdleInhibitDataRecord *record;
+ IdleInhibitor *inhibitor, *last;
+
+ record = data;
+
+ /* Loop through each idle inhibitor. Unlink it. */
+ inhibitor = record->inhibitors.next;
+ while (inhibitor != &record->inhibitors)
+ {
+ last = inhibitor;
+ inhibitor = inhibitor->next;
+
+ last->next = NULL;
+ last->last = NULL;
+ last->global_next->global_last = last->global_last;
+ last->global_last->global_next = last->global_next;
+ last->surface = NULL;
+ }
+
+ /* Check if any idle inhibitors are still active. */
+ DetectSurfaceIdleInhibit ();
+}
+
+static void
+InitIdleInhibitData (IdleInhibitDataRecord *record)
+{
+ if (record->inhibitors.next)
+ /* The data is already initialized. */
+ return;
+
+ record->inhibitors.next = &record->inhibitors;
+ record->inhibitors.last = &record->inhibitors;
+}
+
+static void
+CreateInhibitor (struct wl_client *client, struct wl_resource *resource,
+ uint32_t id, struct wl_resource *surface_resource)
+{
+ Surface *surface;
+ IdleInhibitor *inhibitor;
+ IdleInhibitDataRecord *record;
+
+ inhibitor = XLSafeMalloc (sizeof *inhibitor);
+
+ if (!inhibitor)
+ {
+ wl_resource_post_no_memory (resource);
+ return;
+ }
+
+ memset (inhibitor, 0, sizeof *inhibitor);
+ inhibitor->resource
+ = wl_resource_create (client, &zwp_idle_inhibitor_v1_interface,
+ wl_resource_get_version (resource), id);
+
+ surface = wl_resource_get_user_data (surface_resource);
+ record = XLSurfaceGetClientData (surface, IdleInhibitData,
+ sizeof *record, FreeIdleInhibitData);
+ InitIdleInhibitData (record);
+
+ /* Set the inhibitor's surface. */
+ inhibitor->surface = surface;
+
+ /* And link it onto the list of all idle inhibitors on both the
+ surface and globally. */
+ inhibitor->next = record->inhibitors.next;
+ inhibitor->last = &record->inhibitors;
+ record->inhibitors.next->last = inhibitor;
+ record->inhibitors.next = inhibitor;
+ inhibitor->global_next = all_inhibitors.global_next;
+ inhibitor->global_last = &all_inhibitors;
+ all_inhibitors.global_next->global_last = inhibitor;
+ all_inhibitors.global_next = inhibitor;
+
+ if (surface->num_focused_seats)
+ /* See the comment at the beginning of the file. */
+ ChangeInhibitionTo (IdleInhibited);
+
+ /* Set the implementation. */
+ wl_resource_set_implementation (inhibitor->resource, &idle_inhibitor_impl,
+ inhibitor, HandleResourceDestroy);
+}
+
+static struct zwp_idle_inhibit_manager_v1_interface idle_inhibit_manager_impl =
+ {
+ .create_inhibitor = CreateInhibitor,
+ };
+
+static void
+HandleBind (struct wl_client *client, void *data,
+ uint32_t version, uint32_t id)
+{
+ struct wl_resource *resource;
+
+ resource = wl_resource_create (client,
+ &zwp_idle_inhibit_manager_v1_interface,
+ version, id);
+
+ if (!resource)
+ {
+ wl_client_post_no_memory (client);
+ return;
+ }
+
+ wl_resource_set_implementation (resource, &idle_inhibit_manager_impl,
+ NULL, NULL);
+}
+
+static char **
+ReadCommandResource (const char *name, const char *class)
+{
+ XrmDatabase rdb;
+ XrmName namelist[3];
+ XrmClass classlist[3];
+ XrmValue value;
+ XrmRepresentation type;
+ char **arguments;
+ size_t num_args;
+
+ rdb = XrmGetDatabase (compositor.display);
+
+ if (!rdb)
+ return NULL;
+
+ namelist[1] = XrmStringToQuark (name);
+ namelist[0] = app_quark;
+ namelist[2] = NULLQUARK;
+
+ classlist[1] = XrmStringToQuark (class);
+ classlist[0] = resource_quark;
+ classlist[2] = NULLQUARK;
+
+ if (XrmQGetResource (rdb, namelist, classlist,
+ &type, &value)
+ && type == QString)
+ {
+ ParseProcessString ((const char *) value.addr,
+ &arguments, &num_args);
+
+ return arguments;
+ }
+
+ return NULL;
+}
+
+static int
+ReadIntegerResource (const char *name, const char *class,
+ int default_value)
+{
+ XrmDatabase rdb;
+ XrmName namelist[3];
+ XrmClass classlist[3];
+ XrmValue value;
+ XrmRepresentation type;
+ int result;
+
+ rdb = XrmGetDatabase (compositor.display);
+
+ if (!rdb)
+ return default_value;
+
+ namelist[1] = XrmStringToQuark (name);
+ namelist[0] = app_quark;
+ namelist[2] = NULLQUARK;
+
+ classlist[1] = XrmStringToQuark (class);
+ classlist[0] = resource_quark;
+ classlist[2] = NULLQUARK;
+
+ if (XrmQGetResource (rdb, namelist, classlist,
+ &type, &value)
+ && type == QString)
+ {
+ result = atoi ((char *) value.addr);
+
+ if (!result)
+ return default_value;
+
+ return result;
+ }
+
+ return default_value;
+}
+
+void
+XLInitIdleInhibit (void)
+{
+ idle_inhibit_manager_global
+ = wl_global_create (compositor.wl_display,
+ &zwp_idle_inhibit_manager_v1_interface,
+ 1, NULL, HandleBind);
+
+ all_inhibitors.global_next = &all_inhibitors;
+ all_inhibitors.global_last = &all_inhibitors;
+
+ /* Read various commands from resources. */
+ inhibit_command = ReadCommandResource ("idleInhibitCommand",
+ "IdleInhibitCommand");
+ timer_command = ReadCommandResource ("idleIntervalCommand",
+ "IdleInhibitCommand");
+ deinhibit_command = ReadCommandResource ("idleDeinhibitCommand",
+ "IdleDeinhibitCommand");
+
+ /* Initialize the default value for timer_seconds. */
+ timer_seconds = ReadIntegerResource ("idleCommandInterval",
+ "IdleCommandInterval",
+ 60);
+
+ /* Initialize the process queue. */
+ process_queue = MakeProcessQueue ();
+}
+
+void
+XLIdleInhibitNoticeSurfaceFocused (Surface *surface)
+{
+ NoticeSurfaceFocused (surface);
+}
+
+void
+XLDetectSurfaceIdleInhibit (void)
+{
+ DetectSurfaceIdleInhibit ();
+}
diff --git a/process.c b/process.c
new file mode 100644
index 0000000..bb25442
--- /dev/null
+++ b/process.c
@@ -0,0 +1,413 @@
+/* Wayland compositor running on top of an X server.
+
+Copyright (C) 2022 to various contributors.
+
+This file is part of 12to11.
+
+12to11 is free software: you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation, either version 3 of the License, or (at your
+option) any later version.
+
+12to11 is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with 12to11. If not, see . */
+
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+#include "compositor.h"
+
+typedef struct _ProcessDescription ProcessDescription;
+
+/* Subprocess control and management. This module implements a
+ "process queue", which is an ordered list of commands to run. */
+
+struct _ProcessDescription
+{
+ /* The next and last descriptions in the queue. */
+ ProcessDescription *next, *last;
+
+ /* NULL-terminated array of arguments. */
+ char **arguments;
+
+ /* Size of the argument list excluding the terminating NULL. */
+ size_t num_arguments;
+};
+
+struct _ProcessQueue
+{
+ /* The next process queue in the chain. */
+ ProcessQueue *next;
+
+ /* List of commands that have not yet been run. */
+ ProcessDescription descriptions;
+
+ /* The process currently being run. SIGCHLD must be blocked while
+ reading from this field. */
+ pid_t process;
+};
+
+/* Whether or not the process queue SIGCHLD handler has been
+ installed. */
+static Bool child_handler_installed;
+
+/* List of all process queues. */
+static ProcessQueue *all_queues;
+
+/* Whether or not child processes should be checked. */
+static volatile sig_atomic_t check_child_processes;
+
+static void
+HandleChild (int signal, siginfo_t *siginfo, void *ucontext)
+{
+ ProcessQueue *considering;
+ int temp_errno, status;
+ pid_t pid;
+
+ /* SIGCHILD should now be blocked here. This function cannot call
+ malloc or any other async-signal unsafe functions. That includes
+ free and posix_spawn, so the queue is drained in ProcessPoll. */
+
+ /* Reap the process(es) that exited. */
+
+ do
+ {
+ temp_errno = errno;
+ pid = TEMP_FAILURE_RETRY (waitpid (-1, &status, WNOHANG));
+ errno = temp_errno;
+
+ if (pid == (pid_t) -1)
+ break;
+
+ considering = all_queues;
+ for (; considering; considering = considering->next)
+ {
+ if (considering->process == pid)
+ {
+ /* This process has finished. Set considering->process to
+ (pid_t) -1. The next queued process will then be run
+ later. */
+ considering->process = (pid_t) -1;
+
+ return;
+ }
+ }
+ }
+ while (pid != -1);
+}
+
+static void
+MaybeInstallChildHandler (void)
+{
+ struct sigaction act;
+
+ /* Install the SIGCHLD handler used to drive the process queues if
+ it has not already been installed. */
+
+ if (child_handler_installed)
+ return;
+
+ child_handler_installed = True;
+ memset (&act, 0, sizeof act);
+
+ act.sa_flags = SA_SIGINFO;
+ act.sa_sigaction = HandleChild;
+
+ if (sigaction (SIGCHLD, &act, NULL))
+ {
+ perror ("sigaction");
+ abort ();
+ }
+}
+
+static void
+Block (sigset_t *oldset)
+{
+ sigset_t sigset;
+
+ sigemptyset (&sigset);
+ sigaddset (&sigset, SIGCHLD);
+
+ if (sigprocmask (SIG_BLOCK, &sigset, oldset))
+ {
+ perror ("sigprocmask");
+ abort ();
+ }
+}
+
+static void
+Unblock (void)
+{
+ sigset_t sigset;
+
+ sigemptyset (&sigset);
+ sigaddset (&sigset, SIGCHLD);
+
+ if (sigprocmask (SIG_UNBLOCK, &sigset, NULL))
+ {
+ perror ("sigprocmask");
+ abort ();
+ }
+}
+
+static void
+RunNext (ProcessQueue *queue)
+{
+ ProcessDescription *description, *last;
+ int rc;
+ pid_t pid;
+
+ description = queue->descriptions.last;
+ while (description != &queue->descriptions)
+ {
+ last = description;
+ description = description->last;
+
+ rc = posix_spawnp (&pid, last->arguments[0], NULL, NULL,
+ last->arguments, environ);
+
+ /* Unlink the description. */
+ last->next->last = last->last;
+ last->last->next = last->next;
+ XLFree (last);
+
+ if (!rc)
+ {
+ /* The child has been spawned. Set queue->process and
+ return. */
+ queue->process = pid;
+ return;
+ }
+ else
+ /* Print an error and continue. */
+ fprintf (stderr, "Subprocess creation failed: %s\n",
+ strerror (errno));
+ }
+}
+
+static void
+ProcessPendingDescriptions (void)
+{
+ ProcessQueue *queue;
+
+ Block (NULL);
+
+ for (queue = all_queues; queue; queue = queue->next)
+ {
+ if (queue->process == (pid_t) -1)
+ RunNext (queue);
+ }
+
+ Unblock ();
+}
+
+static void
+ProcessEscapes (char *string)
+{
+ int i, j;
+ Bool escaped;
+
+ /* Naively process escapes in STRING. */
+ escaped = False;
+ i = 0;
+ j = 0;
+
+ while (string[j] != '\0')
+ {
+ if (escaped)
+ {
+ string[i++] = string[j];
+ escaped = False;
+ }
+ else if (*string == '\\')
+ escaped = True;
+ else
+ string[i++] = string[j];
+
+ j++;
+ }
+
+ string[i] = '\0';
+}
+
+void
+ParseProcessString (const char *string, char ***arguments_return,
+ size_t *arg_count)
+{
+ char **arguments;
+ const char *start;
+ Bool escaped, quoted, non_whitespace_seen;
+ size_t nargs;
+
+ /* This is the NULL termination. */
+ arguments = XLCalloc (1, sizeof *arguments);
+ nargs = 0;
+ start = string;
+ escaped = False;
+ quoted = False;
+ non_whitespace_seen = False;
+
+#define AppendArg() \
+ do \
+ { \
+ arguments \
+ = XLRealloc (arguments, \
+ sizeof *arguments * (++nargs + 1)); \
+ arguments[nargs - 1] = XLMalloc (string - start + 1); \
+ memcpy (arguments[nargs - 1], start, string - start); \
+ arguments[nargs - 1][string - start] = '\0'; \
+ ProcessEscapes (arguments[nargs - 1]); \
+ } \
+ while (0)
+
+ while (*string != '\0')
+ {
+ if (!escaped)
+ {
+ if (*string == '\\')
+ escaped = True;
+
+ if (*string == '"')
+ {
+ if (!quoted)
+ {
+ quoted = True;
+
+ if (non_whitespace_seen)
+ AppendArg ();
+
+ start = string + 1;
+ non_whitespace_seen = False;
+ }
+ else
+ {
+ quoted = False;
+
+ /* Append the argument now. */
+ AppendArg ();
+
+ /* Set start to the character after string. */
+ start = string + 1;
+ non_whitespace_seen = False;
+ }
+ }
+ else if (!quoted)
+ {
+ if (*string == ' ')
+ {
+ if (non_whitespace_seen)
+ AppendArg ();
+
+ start = string + 1;
+ non_whitespace_seen = False;
+ }
+ else
+ non_whitespace_seen = True;
+ }
+ }
+ else
+ {
+ escaped = False;
+ non_whitespace_seen = True;
+ }
+
+ string++;
+
+ if (*string == '\0' && non_whitespace_seen)
+ AppendArg ();
+ }
+
+#undef AppendArg
+
+ /* NULL-terminate the argument array. */
+ arguments[nargs] = NULL;
+
+ if (arg_count)
+ *arg_count = nargs;
+
+ *arguments_return = arguments;
+}
+
+void
+RunProcess (ProcessQueue *queue, char **arguments)
+{
+ ProcessDescription *desc;
+
+ MaybeInstallChildHandler ();
+
+ if (!arguments[0])
+ /* There is no executable, so just return. */
+ return;
+
+ /* First, allocate a process description. */
+ desc = XLMalloc (sizeof *desc);
+
+ /* Link it onto the queue. */
+ desc->next = queue->descriptions.next;
+ desc->last = &queue->descriptions;
+
+ /* Save the command line. */
+ desc->arguments = arguments;
+
+ /* Determine how many arguments there are. Note that the caller
+ owns the command line. */
+ desc->num_arguments = 0;
+
+ while (arguments[desc->num_arguments])
+ ++desc->num_arguments;
+
+ /* Finish the link. */
+ queue->descriptions.next->last = desc;
+ queue->descriptions.next = desc;
+
+ /* Process pending process descriptions. */
+ ProcessPendingDescriptions ();
+}
+
+ProcessQueue *
+MakeProcessQueue (void)
+{
+ ProcessQueue *queue;
+
+ queue = XLCalloc (1, sizeof *queue);
+ queue->next = all_queues;
+ queue->descriptions.next = &queue->descriptions;
+ queue->descriptions.last = &queue->descriptions;
+ queue->process = (pid_t) -1;
+
+ Block (NULL);
+ all_queues = queue;
+ Unblock ();
+
+ return queue;
+}
+
+int
+ProcessPoll (struct pollfd *fds, nfds_t nfds,
+ struct timespec *timeout)
+{
+ sigset_t oldset;
+ int rc;
+
+ /* Block SIGPOLL. If SIGCHLD arrives before, then the process will
+ be run by ProcessPendingDescriptions. If it arrives after, then
+ ppoll will be interrupted with EINTR. */
+ Block (&oldset);
+ ProcessPendingDescriptions ();
+ rc = ppoll (fds, nfds, timeout, &oldset);
+ Unblock ();
+
+ return rc;
+}