12to11/process.c
hujianwei a63a7ebdd6 Fix frame timing prediction algorithm
* compositor.h (struct _RoleFuncs): Replace subframe with
`subsurface_update'.
* dmabuf.c (ExistingModifier):
* fns.c (RebalanceBusfault): Pacify various compiler warnings.
* frame_clock.c (struct _FrameClock, PostEndFrame)
(XLFrameClockHandleFrameEvent, XLMakeFrameClockForWindow): Fix
presentation logic.  First, store absolute presentation
timestamps.  Secondly, use the frame_delay to adjust timestamps.
* icon_surface.c (Subframe): Delete function.
(SubsurfaceUpdate): New function.
(EndSubframe): Delete function.
(XLGetIconSurface): Change attached functions.
* picture_renderer.c (struct _PictureTarget, RecordBufferActivity)
(HandleActivityEvent, DestroyRenderTarget)
(FillBoxesWithTransparency, Composite, FinishRender)
(picture_render_funcs): Send roundtrip event only once, and
flush upon FinishRender.
* process.c (ProcessPendingDescriptions, RunProcess,
ProcessPoll): Don't block signals where unnecessary.
* seat.c (struct _Seat, CursorFromRole, ReleaseSeat, Subframe)
(EndSubframe, MakeCurrentCursor, HandleBind, MakeSeatForDevicePair)
(StartResizeTracking):
* subsurface.c (Subframe, EndSubframe, Commit, Teardown)
(GetSubsurface):
* surface.c (HandleScaleChanged):
* xdg_surface.c (CheckFrame, Commit, Subframe, SubsurfaceUpdate)
(EndSubframe, XLGetXdgSurface):
* xdg_toplevel.c (Unmap): Adjust for above changes, and fix some
compiler warnings.
2022-10-24 03:45:57 +00:00

415 lines
8.5 KiB
C

/* 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 <https://www.gnu.org/licenses/>. */
#include <sys/resource.h>
#include <sys/errno.h>
#include <sys/wait.h>
#include <signal.h>
#include <string.h>
#include <spawn.h>
#include <stdio.h>
#include <unistd.h>
#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 (Bool need_block)
{
ProcessQueue *queue;
if (need_block)
Block (NULL);
for (queue = all_queues; queue; queue = queue->next)
{
if (queue->process == (pid_t) -1)
RunNext (queue);
}
if (need_block)
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 (True);
}
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 (False);
rc = ppoll (fds, nfds, timeout, &oldset);
Unblock ();
return rc;
}